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
Adam Osuchowski
Kea
Commits
b82db9d6
Commit
b82db9d6
authored
Apr 11, 2013
by
Tomek Mrugalski
🛰
Browse files
[2898] Support for v6 relayed traffic added.
parent
66d1631e
Changes
14
Hide whitespace changes
Inline
Side-by-side
src/bin/dhcp6/config_parser.cc
View file @
b82db9d6
...
...
@@ -1481,11 +1481,27 @@ private:
std
::
string
iface
;
try
{
iface
=
string_values_
.
getParam
(
"interface"
);
}
catch
(
DhcpConfigError
)
{
}
catch
(
const
DhcpConfigError
&
)
{
// iface not mandatory so swallow the exception
}
/// @todo: Convert this to logger once the parser is working reliably
// Get interface-id option content. For now we support string
// represenation only
std
::
string
ifaceid
;
try
{
ifaceid
=
string_values_
.
getParam
(
"interface-id"
);
}
catch
(
DhcpConfigError
)
{
// interface-id is not mandatory
}
if
(
!
iface
.
empty
()
&&
!
ifaceid
.
empty
())
{
isc_throw
(
isc
::
dhcp
::
DhcpConfigError
,
"parser error: interface (defined for locally reachable "
"subnets) and interface-id (defined for subnets reachable"
" via relays) cannot be defined at the same time for "
"subnet "
<<
addr
.
toText
()
<<
"/"
<<
(
int
)
len
);
}
stringstream
tmp
;
tmp
<<
addr
.
toText
()
<<
"/"
<<
(
int
)
len
<<
" with params t1="
<<
t1
<<
", t2="
<<
t2
<<
", pref="
...
...
@@ -1512,6 +1528,13 @@ private:
subnet_
->
setIface
(
iface
);
}
// Configure interface-id for remote interfaces, if defined
if
(
!
ifaceid
.
empty
())
{
OptionBuffer
tmp
(
ifaceid
.
begin
(),
ifaceid
.
end
());
OptionPtr
opt
(
new
Option
(
Option
::
V6
,
D6O_INTERFACE_ID
,
tmp
));
subnet_
->
setInterfaceId
(
opt
);
}
// We are going to move configured options to the Subnet object.
// Configured options reside in the container where options
// are grouped by space names. Thus we need to get all space names
...
...
@@ -1591,6 +1614,7 @@ private:
factories
[
"pool"
]
=
PoolParser
::
factory
;
factories
[
"option-data"
]
=
OptionDataListParser
::
factory
;
factories
[
"interface"
]
=
StringParser
::
factory
;
factories
[
"interface-id"
]
=
StringParser
::
factory
;
FactoryMap
::
iterator
f
=
factories
.
find
(
config_id
);
if
(
f
==
factories
.
end
())
{
...
...
src/bin/dhcp6/dhcp6.spec
View file @
b82db9d6
...
...
@@ -199,6 +199,12 @@
"item_default": ""
},
{ "item_name": "interface-id",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "renew-timer",
"item_type": "integer",
"item_optional": false,
...
...
src/bin/dhcp6/dhcp6_srv.cc
View file @
b82db9d6
...
...
@@ -403,8 +403,13 @@ Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
if
(
clientid
)
{
answer
->
addOption
(
clientid
);
}
/// @todo: Should throw if there is no client-id (except anonymous INF-REQUEST)
// if this is a relayed message, we need to copy relay information
if
(
!
question
->
relay_info_
.
empty
())
{
answer
->
copyRelayInfo
(
question
);
}
// TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
}
void
...
...
@@ -523,18 +528,43 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
Subnet6Ptr
Dhcpv6Srv
::
selectSubnet
(
const
Pkt6Ptr
&
question
)
{
/// @todo: pass interface information only if received direct (non-relayed) message
Subnet6Ptr
subnet
;
// Try to find a subnet if received packet from a directly connected client
Subnet6Ptr
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
question
->
getIface
());
if
(
subnet
)
{
if
(
question
->
relay_info_
.
empty
())
{
// This is a direct (non-relayed) message
// Try to find a subnet if received packet from a directly connected client
Subnet6Ptr
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
question
->
getIface
());
if
(
subnet
)
{
return
(
subnet
);
}
// If no subnet was found, try to find it based on remote address
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
question
->
getRemoteAddr
());
return
(
subnet
);
}
}
else
{
// If no subnet was found, try to find it based on remote address
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
question
->
getRemoteAddr
());
// This is a relayed message
OptionPtr
interface_id
=
question
->
getAnyRelayOption
(
D6O_INTERFACE_ID
,
Pkt6
::
RELAY_SEARCH_FIRST
);
if
(
interface_id
)
{
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
interface_id
);
}
if
(
subnet
)
{
return
(
subnet
);
}
// If no interface-id was specified (or not configured on server), let's
// try address matching
IOAddress
link_addr
=
question
->
relay_info_
.
back
().
linkaddr_
;
return
(
subnet
);
// if relay filled in link_addr field, then let's use it
if
(
link_addr
!=
IOAddress
(
"::"
))
{
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
link_addr
);
}
return
(
subnet
);
}
}
void
...
...
src/bin/dhcp6/tests/config_parser_unittest.cc
View file @
b82db9d6
...
...
@@ -500,6 +500,110 @@ TEST_F(Dhcp6ParserTest, interfaceGlobal) {
EXPECT_EQ
(
1
,
rcode_
);
}
// This test checks if it is possible to define a subnet with an
// interface-id option defined.
TEST_F
(
Dhcp6ParserTest
,
subnetInterfaceId
)
{
const
string
valid_interface_id
=
"foobar"
;
const
string
bogus_interface_id
=
"blah"
;
ConstElementPtr
status
;
// There should be at least one interface
string
config
=
"{ "
"
\"
preferred-lifetime
\"
: 3000,"
"
\"
rebind-timer
\"
: 2000, "
"
\"
renew-timer
\"
: 1000, "
"
\"
subnet6
\"
: [ { "
"
\"
pool
\"
: [
\"
2001:db8:1::1 - 2001:db8:1::ffff
\"
],"
"
\"
interface-id
\"
:
\"
"
+
valid_interface_id
+
"
\"
,"
"
\"
subnet
\"
:
\"
2001:db8:1::/64
\"
} ],"
"
\"
valid-lifetime
\"
: 4000 }"
;
cout
<<
config
<<
endl
;
ElementPtr
json
=
Element
::
fromJSON
(
config
);
EXPECT_NO_THROW
(
status
=
configureDhcp6Server
(
srv_
,
json
));
// returned value should be 0 (configuration success)
ASSERT_TRUE
(
status
);
comment_
=
parseAnswer
(
rcode_
,
status
);
EXPECT_EQ
(
0
,
rcode_
);
// try to get a subnet based on bogus interface-id option
OptionBuffer
tmp
(
bogus_interface_id
.
begin
(),
bogus_interface_id
.
end
());
OptionPtr
ifaceid
(
new
Option
(
Option
::
V6
,
D6O_INTERFACE_ID
,
tmp
));
Subnet6Ptr
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
ifaceid
);
EXPECT_FALSE
(
subnet
);
// now try to get subnet for valid interface-id value
tmp
=
OptionBuffer
(
valid_interface_id
.
begin
(),
valid_interface_id
.
end
());
ifaceid
.
reset
(
new
Option
(
Option
::
V6
,
D6O_INTERFACE_ID
,
tmp
));
subnet
=
CfgMgr
::
instance
().
getSubnet6
(
ifaceid
);
ASSERT_TRUE
(
subnet
);
EXPECT_TRUE
(
ifaceid
->
equal
(
subnet
->
getInterfaceId
()));
}
// This test checks if it is not allowed to define global interface
// parameter.
TEST_F
(
Dhcp6ParserTest
,
interfaceIdGlobal
)
{
ConstElementPtr
status
;
string
config
=
"{
\"
interface
\"
: [
\"
all
\"
],"
"
\"
preferred-lifetime
\"
: 3000,"
"
\"
rebind-timer
\"
: 2000, "
"
\"
renew-timer
\"
: 1000, "
"
\"
interface-id
\"
:
\"
foobar
\"
,"
// Not valid. Can be defined in subnet only
"
\"
subnet6
\"
: [ { "
"
\"
pool
\"
: [
\"
2001:db8:1::1 - 2001:db8:1::ffff
\"
],"
"
\"
subnet
\"
:
\"
2001:db8:1::/64
\"
} ],"
"
\"
valid-lifetime
\"
: 4000 }"
;
cout
<<
config
<<
endl
;
ElementPtr
json
=
Element
::
fromJSON
(
config
);
EXPECT_NO_THROW
(
status
=
configureDhcp6Server
(
srv_
,
json
));
// returned value should be 1 (parse error)
ASSERT_TRUE
(
status
);
comment_
=
parseAnswer
(
rcode_
,
status
);
EXPECT_EQ
(
1
,
rcode_
);
}
// This test checks if it is not possible to define a subnet with an
// interface (i.e. local subnet) and interface-id (remote subnet) defined.
TEST_F
(
Dhcp6ParserTest
,
subnetInterfaceAndInterfaceId
)
{
ConstElementPtr
status
;
string
config
=
"{
\"
preferred-lifetime
\"
: 3000,"
"
\"
rebind-timer
\"
: 2000, "
"
\"
renew-timer
\"
: 1000, "
"
\"
subnet6
\"
: [ { "
"
\"
pool
\"
: [
\"
2001:db8:1::1 - 2001:db8:1::ffff
\"
],"
"
\"
interface
\"
:
\"
"
+
valid_iface_
+
"
\"
,"
"
\"
interface-id
\"
:
\"
foobar
\"
,"
"
\"
subnet
\"
:
\"
2001:db8:1::/64
\"
} ],"
"
\"
valid-lifetime
\"
: 4000 }"
;
cout
<<
config
<<
endl
;
ElementPtr
json
=
Element
::
fromJSON
(
config
);
EXPECT_NO_THROW
(
status
=
configureDhcp6Server
(
srv_
,
json
));
// returned value should be 0 (configuration success)
ASSERT_TRUE
(
status
);
comment_
=
parseAnswer
(
rcode_
,
status
);
EXPECT_EQ
(
1
,
rcode_
);
}
// Test verifies that a subnet with pool values that do not belong to that
// pool are rejected.
TEST_F
(
Dhcp6ParserTest
,
poolOutOfSubnet
)
{
...
...
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
View file @
b82db9d6
...
...
@@ -98,6 +98,16 @@ public:
return
(
ia
);
}
/// @brief generates interface-id option, based on text
///
/// @param iface_id textual representation of the interface-id content
///
/// @return pointer to the option object
OptionPtr
generateInterfaceId
(
const
string
&
iface_id
)
{
OptionBuffer
tmp
(
iface_id
.
begin
(),
iface_id
.
end
());
return
OptionPtr
(
new
Option
(
Option
::
V6
,
D6O_INTERFACE_ID
,
tmp
));
}
// Generate client-id option
OptionPtr
generateClientId
(
size_t
duid_size
=
32
)
{
...
...
@@ -678,6 +688,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
// check that IA_NA was returned and that there's an address included
boost
::
shared_ptr
<
Option6IAAddr
>
addr
=
checkIA_NA
(
reply
,
234
,
subnet_
->
getT1
(),
subnet_
->
getT2
());
ASSERT_TRUE
(
addr
);
// Check that the assigned address is indeed from the configured pool
checkIAAddr
(
addr
,
addr
->
getAddress
(),
subnet_
->
getPreferred
(),
subnet_
->
getValid
());
...
...
@@ -731,6 +742,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
// check that IA_NA was returned and that there's an address included
boost
::
shared_ptr
<
Option6IAAddr
>
addr
=
checkIA_NA
(
reply
,
234
,
subnet_
->
getT1
(),
subnet_
->
getT2
());
ASSERT_TRUE
(
addr
);
// check that we've got the address we requested
checkIAAddr
(
addr
,
hint
,
subnet_
->
getPreferred
(),
subnet_
->
getValid
());
...
...
@@ -779,6 +791,7 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
// check that IA_NA was returned and that there's an address included
boost
::
shared_ptr
<
Option6IAAddr
>
addr
=
checkIA_NA
(
reply
,
234
,
subnet_
->
getT1
(),
subnet_
->
getT2
());
ASSERT_TRUE
(
addr
);
// Check that the assigned address is indeed from the configured pool
checkIAAddr
(
addr
,
addr
->
getAddress
(),
subnet_
->
getPreferred
(),
subnet_
->
getValid
());
...
...
@@ -840,6 +853,9 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
subnet_
->
getT2
());
boost
::
shared_ptr
<
Option6IAAddr
>
addr3
=
checkIA_NA
(
reply3
,
3
,
subnet_
->
getT1
(),
subnet_
->
getT2
());
ASSERT_TRUE
(
addr1
);
ASSERT_TRUE
(
addr2
);
ASSERT_TRUE
(
addr3
);
// Check that the assigned address is indeed from the configured pool
checkIAAddr
(
addr1
,
addr1
->
getAddress
(),
subnet_
->
getPreferred
(),
subnet_
->
getValid
());
...
...
@@ -910,6 +926,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
// check that IA_NA was returned and that there's an address included
boost
::
shared_ptr
<
Option6IAAddr
>
addr
=
checkIA_NA
(
reply
,
234
,
subnet_
->
getT1
(),
subnet_
->
getT2
());
ASSERT_TRUE
(
addr
);
// check that we've got the address we requested
checkIAAddr
(
addr
,
hint
,
subnet_
->
getPreferred
(),
subnet_
->
getValid
());
...
...
@@ -1592,6 +1609,113 @@ TEST_F(Dhcpv6SrvTest, selectSubnetIface) {
EXPECT_EQ
(
subnet3
,
srv
.
selectSubnet
(
pkt
));
}
// This test verifies if selectSubnet() selects proper subnet for a given
// linkaddr in RELAY-FORW message
TEST_F
(
Dhcpv6SrvTest
,
selectSubnetRelayLinkaddr
)
{
NakedDhcpv6Srv
srv
(
0
);
Subnet6Ptr
subnet1
(
new
Subnet6
(
IOAddress
(
"2001:db8:1::"
),
48
,
1
,
2
,
3
,
4
));
Subnet6Ptr
subnet2
(
new
Subnet6
(
IOAddress
(
"2001:db8:2::"
),
48
,
1
,
2
,
3
,
4
));
Subnet6Ptr
subnet3
(
new
Subnet6
(
IOAddress
(
"2001:db8:3::"
),
48
,
1
,
2
,
3
,
4
));
Pkt6
::
RelayInfo
relay
;
relay
.
linkaddr_
=
IOAddress
(
"2001:db8:2::1234"
);
relay
.
peeraddr_
=
IOAddress
(
"fe80::1"
);
// CASE 1: We have only one subnet defined and we received relayed traffic.
// The only available subnet should NOT be selected.
CfgMgr
::
instance
().
deleteSubnets6
();
CfgMgr
::
instance
().
addSubnet6
(
subnet1
);
// just a single subnet
Pkt6Ptr
pkt
=
Pkt6Ptr
(
new
Pkt6
(
DHCPV6_SOLICIT
,
1234
));
pkt
->
relay_info_
.
push_back
(
relay
);
Subnet6Ptr
selected
=
srv
.
selectSubnet
(
pkt
);
EXPECT_FALSE
(
selected
);
// CASE 2: We have three subnets defined and we received relayed traffic.
// Nothing should be selected.
CfgMgr
::
instance
().
deleteSubnets6
();
CfgMgr
::
instance
().
addSubnet6
(
subnet1
);
CfgMgr
::
instance
().
addSubnet6
(
subnet2
);
CfgMgr
::
instance
().
addSubnet6
(
subnet3
);
selected
=
srv
.
selectSubnet
(
pkt
);
EXPECT_EQ
(
selected
,
subnet2
);
// CASE 3: We have three subnets defined and we received relayed traffic
// that came out of subnet 2. We should select subnet2 then
CfgMgr
::
instance
().
deleteSubnets6
();
CfgMgr
::
instance
().
addSubnet6
(
subnet1
);
CfgMgr
::
instance
().
addSubnet6
(
subnet2
);
CfgMgr
::
instance
().
addSubnet6
(
subnet3
);
// source of the packet should have no meaning. Selection is based
// on linkaddr field in the relay
pkt
->
setRemoteAddr
(
IOAddress
(
"2001:db8:1::baca"
));
selected
=
srv
.
selectSubnet
(
pkt
);
EXPECT_EQ
(
selected
,
subnet2
);
// CASE 4: We have three subnets defined and we received relayed traffic
// that came out of undefined subnet. We should select nothing
CfgMgr
::
instance
().
deleteSubnets6
();
CfgMgr
::
instance
().
addSubnet6
(
subnet1
);
CfgMgr
::
instance
().
addSubnet6
(
subnet2
);
CfgMgr
::
instance
().
addSubnet6
(
subnet3
);
pkt
->
relay_info_
.
clear
();
relay
.
linkaddr_
=
IOAddress
(
"2001:db8:4::1234"
);
pkt
->
relay_info_
.
push_back
(
relay
);
selected
=
srv
.
selectSubnet
(
pkt
);
EXPECT_FALSE
(
selected
);
}
// This test verifies if selectSubnet() selects proper subnet for a given
// interface-id option
TEST_F
(
Dhcpv6SrvTest
,
selectSubnetRelayInterfaceId
)
{
NakedDhcpv6Srv
srv
(
0
);
Subnet6Ptr
subnet1
(
new
Subnet6
(
IOAddress
(
"2001:db8:1::"
),
48
,
1
,
2
,
3
,
4
));
Subnet6Ptr
subnet2
(
new
Subnet6
(
IOAddress
(
"2001:db8:2::"
),
48
,
1
,
2
,
3
,
4
));
Subnet6Ptr
subnet3
(
new
Subnet6
(
IOAddress
(
"2001:db8:3::"
),
48
,
1
,
2
,
3
,
4
));
subnet1
->
setInterfaceId
(
generateInterfaceId
(
"relay1"
));
subnet2
->
setInterfaceId
(
generateInterfaceId
(
"relay2"
));
// CASE 1: We have only one subnet defined and it is for interface-id "relay1"
// Packet came with interface-id "relay2". We should not select subnet1
CfgMgr
::
instance
().
deleteSubnets6
();
CfgMgr
::
instance
().
addSubnet6
(
subnet1
);
// just a single subnet
Pkt6Ptr
pkt
=
Pkt6Ptr
(
new
Pkt6
(
DHCPV6_SOLICIT
,
1234
));
Pkt6
::
RelayInfo
relay
;
relay
.
linkaddr_
=
IOAddress
(
"2001:db8:2::1234"
);
relay
.
peeraddr_
=
IOAddress
(
"fe80::1"
);
OptionPtr
opt
=
generateInterfaceId
(
"relay2"
);
relay
.
options_
.
insert
(
pair
<
int
,
OptionPtr
>
(
opt
->
getType
(),
opt
));
pkt
->
relay_info_
.
push_back
(
relay
);
// There is only one subnet configured and we are outside of that subnet
Subnet6Ptr
selected
=
srv
.
selectSubnet
(
pkt
);
EXPECT_FALSE
(
selected
);
// CASE 2: We have only one subnet defined and it is for interface-id "relay2"
// Packet came with interface-id "relay2". We should select it
CfgMgr
::
instance
().
deleteSubnets6
();
CfgMgr
::
instance
().
addSubnet6
(
subnet2
);
// just a single subnet
selected
=
srv
.
selectSubnet
(
pkt
);
EXPECT_EQ
(
selected
,
subnet2
);
// CASE 3: We have only 3 subnets defined: one remote for interface-id "relay1",
// one remote for interface-id "relay2" and third local
// packet comes with interface-id "relay2". We should select subnet2
CfgMgr
::
instance
().
deleteSubnets6
();
CfgMgr
::
instance
().
addSubnet6
(
subnet1
);
CfgMgr
::
instance
().
addSubnet6
(
subnet2
);
CfgMgr
::
instance
().
addSubnet6
(
subnet3
);
EXPECT_EQ
(
subnet2
,
srv
.
selectSubnet
(
pkt
));
}
// This test verifies if the server-id disk operations (read, write) are
// working properly.
TEST_F
(
Dhcpv6SrvTest
,
ServerID
)
{
...
...
src/lib/dhcp/pkt6.cc
View file @
b82db9d6
...
...
@@ -72,6 +72,60 @@ uint16_t Pkt6::len() {
}
}
OptionPtr
Pkt6
::
getAnyRelayOption
(
uint16_t
opt_type
,
RelaySearchOrder
order
)
{
int
start
=
0
;
int
end
=
0
;
int
direction
=
0
;
if
(
relay_info_
.
empty
())
{
// there's no relay info, this is a direct message
return
(
OptionPtr
());
}
switch
(
order
)
{
case
RELAY_SEARCH_FROM_CLIENT
:
// search backwards
start
=
relay_info_
.
size
()
-
1
;
end
=
0
;
direction
=
-
1
;
break
;
case
RELAY_SEARCH_FROM_SERVER
:
// search forward
start
=
0
;
end
=
relay_info_
.
size
()
-
1
;
direction
=
1
;
break
;
case
RELAY_SEARCH_FIRST
:
// look at the innermost relay only
start
=
relay_info_
.
size
()
-
1
;
end
=
start
;
direction
=
0
;
break
;
case
RELAY_SEARCH_LAST
:
// look at the outermost relay only
start
=
0
;
end
=
0
;
direction
=
0
;
}
// this is a tricky loop. It must go from start to end, but it must work in
// both directions (start > end; or start < end). We can't use regular
// exit condition, because we don't know whether to use i <= end or i >= end
for
(
int
i
=
start
;
;
i
+=
direction
)
{
OptionPtr
opt
=
getRelayOption
(
opt_type
,
i
);
if
(
opt
)
{
return
(
opt
);
}
if
(
i
==
end
)
{
break
;
}
}
return
getRelayOption
(
opt_type
,
end
);
}
OptionPtr
Pkt6
::
getRelayOption
(
uint16_t
opt_type
,
uint8_t
relay_level
)
{
if
(
relay_level
>=
relay_info_
.
size
())
{
isc_throw
(
OutOfRange
,
"This message was relayed "
<<
relay_info_
.
size
()
<<
" time(s)."
...
...
@@ -483,5 +537,32 @@ const char* Pkt6::getName() const {
return
(
getName
(
getType
()));
}
void
Pkt6
::
copyRelayInfo
(
const
Pkt6Ptr
&
question
)
{
// we use index rather than iterator, because we need that as a parameter
// passed to getRelayOption()
for
(
int
i
=
0
;
i
<
question
->
relay_info_
.
size
();
++
i
)
{
RelayInfo
x
;
x
.
msg_type_
=
DHCPV6_RELAY_REPL
;
x
.
hop_count_
=
question
->
relay_info_
[
i
].
hop_count_
;
x
.
linkaddr_
=
question
->
relay_info_
[
i
].
linkaddr_
;
x
.
peeraddr_
=
question
->
relay_info_
[
i
].
peeraddr_
;
// Is there interface-id option in this nesting level if there is,
// we need to echo it back
OptionPtr
opt
=
question
->
getRelayOption
(
D6O_INTERFACE_ID
,
i
);
// taken from question->RelayInfo_[i].options_
if
(
opt
)
{
x
.
options_
.
insert
(
pair
<
int
,
boost
::
shared_ptr
<
Option
>
>
(
opt
->
getType
(),
opt
));
}
/// @todo: implement support for ERO (Echo Request Option, RFC4994)
// add this relay-repl info to our message
relay_info_
.
push_back
(
x
);
}
}
}
// end of isc::dhcp namespace
}
// end of isc namespace
src/lib/dhcp/pkt6.h
View file @
b82db9d6
...
...
@@ -30,6 +30,9 @@ namespace isc {
namespace
dhcp
{
class
Pkt6
;
typedef
boost
::
shared_ptr
<
Pkt6
>
Pkt6Ptr
;
class
Pkt6
{
public:
/// specifies non-relayed DHCPv6 packet header length (over UDP)
...
...
@@ -44,6 +47,28 @@ public:
TCP
=
1
// there are TCP DHCPv6 packets (bulk leasequery, failover)
};
/// @brief defines relay search pattern
///
/// Defines order in which options are searched in a message that
/// passed through mulitple relays. RELAY_SEACH_FROM_CLIENT will
/// start search from the relay that was the closest to the client
/// (i.e. innermost in the encapsulated message, which also means
/// this was the first relay that forwarded packet received by the
/// server and this will be the last relay that will handle the
/// response that server sent towards the client.).
/// RELAY_SEARCH_FROM_SERVER is the opposite. This will be the
/// relay closest to the server (i.e. outermost in the encapsulated
/// message, which also means it was the last relay that relayed
/// the received message and will be the first one to process
/// server's response). RELAY_SEARCH_FIRST is option from the first
/// relay (closest to the client), RELAY_SEARCH_LAST is the
/// last relay (closest to the server).
enum
RelaySearchOrder
{
RELAY_SEARCH_FROM_CLIENT
=
1
,
RELAY_SEARCH_FROM_SERVER
=
2
,
RELAY_SEARCH_FIRST
=
3
,
RELAY_SEARCH_LAST
=
4
};
/// @brief structure that describes a single relay information
///
...
...
@@ -201,6 +226,18 @@ public:
/// @return pointer to the option (or NULL if there is no such option)
OptionPtr
getRelayOption
(
uint16_t
option_code
,
uint8_t
nesting_level
);
/// @brief returns first instance of a specified option
///
/// When client's packet traverses multiple relays, each passing relay
/// may insert extra options. This method allows getting specific instance
/// of a given option (closest to the client, closest to the server, etc.)
/// See @ref RelaySearchOrder for detailed description.
///
/// @param option_code searched option
/// @param order option search order (see @ref RelaySearchOrder)
/// @return option pointer (or NULL if no option matches specified criteria)
OptionPtr
getAnyRelayOption
(
uint16_t
option_code
,
RelaySearchOrder
order
);
/// @brief Returns all instances of specified type.
///
/// Returns all instances of options of the specified type. DHCPv6 protocol
...
...
@@ -356,6 +393,14 @@ public:
/// be freed by the caller.
const
char
*
getName
()
const
;
/// @brief copies relay information from client's packet to server's response
///
/// This information is not simply copied over. Some parameter are
/// removed, msg_type_is updated (RELAY-FORW => RELAY-REPL), etc.
///
/// @param question client's packet
void
copyRelayInfo
(
const
Pkt6Ptr
&
question
);
/// relay information
///
/// this is a public field. Otherwise we hit one of the two problems:
...
...
@@ -494,8 +539,6 @@ protected:
boost
::
posix_time
::
ptime
timestamp_
;
};
// Pkt6 class
typedef
boost
::
shared_ptr
<
Pkt6
>
Pkt6Ptr
;
}
// isc::dhcp namespace
}
// isc namespace
...
...
src/lib/dhcp/tests/pkt6_unittest.cc
View file @
b82db9d6
...
...
@@ -22,6 +22,7 @@
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/pkt6.h>
#include <util/range_utilities.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/scoped_ptr.hpp>
...
...
@@ -44,6 +45,18 @@ class Pkt6Test : public ::testing::Test {
public:
Pkt6Test
()
{
}
/// @brief generates an option with given code (and length) and random content
///
/// @param code option code
/// @param len data length (data will be randomized)
///
/// @return pointer to the new option
OptionPtr
generateRandomOption
(
uint16_t
code
,
size_t
len
=
10
)
{
OptionBuffer
data
(
len
);
util
::
fillRandom
(
data
.
begin
(),
data
.
end
());