Skip to content
GitLab
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
5ed8d736
Commit
5ed8d736
authored
Mar 12, 2015
by
Marcin Siodelski
Browse files
[3688] Avoid multiple calls to selectSubnet by the DHCPv4 server.
parent
a9566567
Changes
5
Hide whitespace changes
Inline
Side-by-side
src/bin/dhcp4/dhcp4_messages.mes
View file @
5ed8d736
...
...
@@ -31,10 +31,6 @@ to establish a session with the Kea control channel.
This debug message informs that incoming packet has been assigned to specified
class or classes. This is a normal behavior and indicates successful operation.
% DHCP4_CLASS_PROCESSING_FAILED client class specific processing failed
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_CLIENT_NAME_PROC_FAIL failed to process the fqdn or hostname sent by a client: %1
This debug message is issued when the DHCP server was unable to process the
FQDN or Hostname option sent by a client. This is likely because the client's
...
...
@@ -78,6 +74,10 @@ change is committed by the administrator.
A debug message indicating that the DHCPv4 server has received an
updated configuration from the Kea configuration system.
% DHCP4_DISCOVER_CLASS_PROCESSING_FAILED client class specific processing failed for DHCPDISCOVER
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_DDNS_REQUEST_SEND_FAILED failed sending a request to kea-dhcp-ddns, error: %1, ncr: %2
This error message indicates that DHCP4 server attempted to send a DDNS
update request to the DHCP-DDNS server. This is most likely a configuration or
...
...
@@ -148,6 +148,10 @@ point, the setting of the flag instructs the server not to choose a
subnet, an action that severely limits further processing; the server
will be only able to offer global options - no addresses will be assigned.
% DHCP4_INFORM_CLASS_PROCESSING_FAILED client class specific processing failed for DHCPINFORM
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_INIT_FAIL failed to initialize Kea server: %1
The server has failed to initialize. This may be because the configuration
was not successful, or it encountered any other critical error on startup.
...
...
@@ -349,6 +353,10 @@ a different hardware address. One possible reason for using different
hardware address is that a cloned virtual machine was not updated and
both clones use the same client-id.
% DHCP4_REQUEST_CLASS_PROCESSING_FAILED client class specific processing failed for DHCPREQUEST
This debug message means that the server processing that is unique for each
client class has reported a failure. The response packet will not be sent.
% DHCP4_RESPONSE_DATA responding with packet type %1, data is <%2>
A debug message listing the data returned to the client.
...
...
src/bin/dhcp4/dhcp4_srv.cc
View file @
5ed8d736
...
...
@@ -85,13 +85,21 @@ namespace isc {
namespace
dhcp
{
Dhcpv4Exchange
::
Dhcpv4Exchange
(
const
AllocEnginePtr
&
alloc_engine
,
const
Pkt4Ptr
&
query
)
const
Pkt4Ptr
&
query
,
const
Subnet4Ptr
&
subnet
)
:
alloc_engine_
(
alloc_engine
),
query_
(
query
),
resp_
(),
context_
(
new
AllocEngine
::
ClientContext4
())
{
if
(
!
alloc_engine_
||
!
query_
)
{
isc_throw
(
BadValue
,
"alloc_engine and query values must not"
" be NULL when creating an instance of the"
" Dhcpv4Exchange"
);
}
// Create response message.
initResponse
();
// Select subnet for the query message.
selectS
ubnet
()
;
context_
->
subnet_
=
s
ubnet
;
// Hardware address.
context_
->
hwaddr_
=
query
->
getHWAddr
();
// Client Identifier
...
...
@@ -122,61 +130,6 @@ Dhcpv4Exchange::initResponse() {
}
}
void
Dhcpv4Exchange
::
selectSubnet
()
{
context_
->
subnet_
=
selectSubnet
(
query_
);
}
Subnet4Ptr
Dhcpv4Exchange
::
selectSubnet
(
const
Pkt4Ptr
&
query
)
{
Subnet4Ptr
subnet
;
SubnetSelector
selector
;
selector
.
ciaddr_
=
query
->
getCiaddr
();
selector
.
giaddr_
=
query
->
getGiaddr
();
selector
.
local_address_
=
query
->
getLocalAddr
();
selector
.
remote_address_
=
query
->
getRemoteAddr
();
selector
.
client_classes_
=
query
->
classes_
;
selector
.
iface_name_
=
query
->
getIface
();
CfgMgr
&
cfgmgr
=
CfgMgr
::
instance
();
subnet
=
cfgmgr
.
getCurrentCfg
()
->
getCfgSubnets4
()
->
selectSubnet
(
selector
);
// Let's execute all callouts registered for subnet4_select
if
(
HooksManager
::
calloutsPresent
(
Hooks
.
hook_index_subnet4_select_
))
{
CalloutHandlePtr
callout_handle
=
getCalloutHandle
(
query
);
// We're reusing callout_handle from previous calls
callout_handle
->
deleteAllArguments
();
// Set new arguments
callout_handle
->
setArgument
(
"query4"
,
query
);
callout_handle
->
setArgument
(
"subnet4"
,
subnet
);
callout_handle
->
setArgument
(
"subnet4collection"
,
cfgmgr
.
getCurrentCfg
()
->
getCfgSubnets4
()
->
getAll
());
// Call user (and server-side) callouts
HooksManager
::
callCallouts
(
Hooks
.
hook_index_subnet4_select_
,
*
callout_handle
);
// Callouts decided to skip this step. This means that no subnet
// will be selected. Packet processing will continue, but it will
// be severely limited (i.e. only global options will be assigned)
if
(
callout_handle
->
getSkip
())
{
LOG_DEBUG
(
dhcp4_logger
,
DBG_DHCP4_HOOKS
,
DHCP4_HOOK_SUBNET4_SELECT_SKIP
);
return
(
Subnet4Ptr
());
}
// Use whatever subnet was specified by the callout
callout_handle
->
getArgument
(
"subnet4"
,
subnet
);
}
return
(
subnet
);
}
const
std
::
string
Dhcpv4Srv
::
VENDOR_CLASS_PREFIX
(
"VENDOR_CLASS_"
);
Dhcpv4Srv
::
Dhcpv4Srv
(
uint16_t
port
,
const
bool
use_bcast
,
...
...
@@ -232,8 +185,53 @@ Dhcpv4Srv::shutdown() {
}
isc
::
dhcp
::
Subnet4Ptr
Dhcpv4Srv
::
selectSubnet
(
const
Pkt4Ptr
&
question
)
{
return
(
Dhcpv4Exchange
::
selectSubnet
(
question
));
Dhcpv4Srv
::
selectSubnet
(
const
Pkt4Ptr
&
query
,
const
bool
run_hooks
)
const
{
Subnet4Ptr
subnet
;
SubnetSelector
selector
;
selector
.
ciaddr_
=
query
->
getCiaddr
();
selector
.
giaddr_
=
query
->
getGiaddr
();
selector
.
local_address_
=
query
->
getLocalAddr
();
selector
.
remote_address_
=
query
->
getRemoteAddr
();
selector
.
client_classes_
=
query
->
classes_
;
selector
.
iface_name_
=
query
->
getIface
();
CfgMgr
&
cfgmgr
=
CfgMgr
::
instance
();
subnet
=
cfgmgr
.
getCurrentCfg
()
->
getCfgSubnets4
()
->
selectSubnet
(
selector
);
// Let's execute all callouts registered for subnet4_select
if
(
run_hooks
&&
HooksManager
::
calloutsPresent
(
hook_index_subnet4_select_
))
{
CalloutHandlePtr
callout_handle
=
getCalloutHandle
(
query
);
// We're reusing callout_handle from previous calls
callout_handle
->
deleteAllArguments
();
// Set new arguments
callout_handle
->
setArgument
(
"query4"
,
query
);
callout_handle
->
setArgument
(
"subnet4"
,
subnet
);
callout_handle
->
setArgument
(
"subnet4collection"
,
cfgmgr
.
getCurrentCfg
()
->
getCfgSubnets4
()
->
getAll
());
// Call user (and server-side) callouts
HooksManager
::
callCallouts
(
hook_index_subnet4_select_
,
*
callout_handle
);
// Callouts decided to skip this step. This means that no subnet
// will be selected. Packet processing will continue, but it will
// be severely limited (i.e. only global options will be assigned)
if
(
callout_handle
->
getSkip
())
{
LOG_DEBUG
(
dhcp4_logger
,
DBG_DHCP4_HOOKS
,
DHCP4_HOOK_SUBNET4_SELECT_SKIP
);
return
(
Subnet4Ptr
());
}
// Use whatever subnet was specified by the callout
callout_handle
->
getArgument
(
"subnet4"
,
subnet
);
}
return
(
subnet
);
}
Pkt4Ptr
...
...
@@ -457,17 +455,6 @@ Dhcpv4Srv::run() {
continue
;
}
// Let's do class specific processing. This is done before
// pkt4_send.
//
/// @todo: decide whether we want to add a new hook point for
/// doing class specific processing.
if
(
!
classSpecificProcessing
(
query
,
rsp
))
{
/// @todo add more verbosity here
LOG_DEBUG
(
dhcp4_logger
,
DBG_DHCP4_BASIC
,
DHCP4_CLASS_PROCESSING_FAILED
);
continue
;
}
// Specifies if server should do the packing
bool
skip_pack
=
false
;
...
...
@@ -1468,9 +1455,9 @@ Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
Pkt4Ptr
Dhcpv4Srv
::
processDiscover
(
Pkt4Ptr
&
discover
)
{
Dhcpv4Exchange
ex
(
alloc_engine_
,
discover
);
sanityCheck
(
discover
,
FORBIDDEN
);
sanityCheck
(
ex
,
FORBIDDEN
);
Dhcpv4Exchange
ex
(
alloc_engine_
,
discover
,
selectSubnet
(
discover
)
);
copyDefaultFields
(
ex
);
appendDefaultOptions
(
ex
);
...
...
@@ -1509,15 +1496,22 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
appendServerID
(
ex
);
/// @todo: decide whether we want to add a new hook point for
/// doing class specific processing.
if
(
!
classSpecificProcessing
(
ex
))
{
/// @todo add more verbosity here
LOG_DEBUG
(
dhcp4_logger
,
DBG_DHCP4_BASIC
,
DHCP4_DISCOVER_CLASS_PROCESSING_FAILED
);
}
return
(
ex
.
getResponse
());
}
Pkt4Ptr
Dhcpv4Srv
::
processRequest
(
Pkt4Ptr
&
request
)
{
Dhcpv4Exchange
ex
(
alloc_engine_
,
request
);
/// @todo Uncomment this (see ticket #3116)
/// sanityCheck(ex, MANDATORY);
/// sanityCheck(request, MANDATORY);
Dhcpv4Exchange
ex
(
alloc_engine_
,
request
,
selectSubnet
(
request
));
copyDefaultFields
(
ex
);
appendDefaultOptions
(
ex
);
...
...
@@ -1554,6 +1548,13 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
appendServerID
(
ex
);
/// @todo: decide whether we want to add a new hook point for
/// doing class specific processing.
if
(
!
classSpecificProcessing
(
ex
))
{
/// @todo add more verbosity here
LOG_DEBUG
(
dhcp4_logger
,
DBG_DHCP4_BASIC
,
DHCP4_REQUEST_CLASS_PROCESSING_FAILED
);
}
return
(
ex
.
getResponse
());
}
...
...
@@ -1675,10 +1676,10 @@ Dhcpv4Srv::processDecline(Pkt4Ptr&) {
Pkt4Ptr
Dhcpv4Srv
::
processInform
(
Pkt4Ptr
&
inform
)
{
Dhcpv4Exchange
ex
(
alloc_engine_
,
inform
);
// DHCPINFORM MUST not include server identifier.
sanityCheck
(
ex
,
FORBIDDEN
);
sanityCheck
(
inform
,
FORBIDDEN
);
Dhcpv4Exchange
ex
(
alloc_engine_
,
inform
,
selectSubnet
(
inform
));
Pkt4Ptr
ack
=
ex
.
getResponse
();
...
...
@@ -1701,6 +1702,14 @@ Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
// The DHCPACK must contain server id.
appendServerID
(
ex
);
/// @todo: decide whether we want to add a new hook point for
/// doing class specific processing.
if
(
!
classSpecificProcessing
(
ex
))
{
/// @todo add more verbosity here
LOG_DEBUG
(
dhcp4_logger
,
DBG_DHCP4_BASIC
,
DHCP4_INFORM_CLASS_PROCESSING_FAILED
);
}
return
(
ex
.
getResponse
());
}
...
...
@@ -1800,7 +1809,7 @@ Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) const {
return
(
false
);
}
return
((
pkt
->
getLocalAddr
()
!=
IOAddress
::
IPV4_BCAST_ADDRESS
()
||
Dhcpv4Exchange
::
selectSubnet
(
pkt
)));
||
selectSubnet
(
pkt
,
false
)));
}
bool
...
...
@@ -1899,14 +1908,14 @@ Dhcpv4Srv::acceptServerId(const Pkt4Ptr& query) const {
}
void
Dhcpv4Srv
::
sanityCheck
(
const
Dhcpv4Exchange
&
ex
,
RequirementLevel
serverid
)
{
OptionPtr
server_id
=
ex
.
getQ
uery
()
->
getOption
(
DHO_DHCP_SERVER_IDENTIFIER
);
Dhcpv4Srv
::
sanityCheck
(
const
Pkt4Ptr
&
query
,
RequirementLevel
serverid
)
{
OptionPtr
server_id
=
q
uery
->
getOption
(
DHO_DHCP_SERVER_IDENTIFIER
);
switch
(
serverid
)
{
case
FORBIDDEN
:
if
(
server_id
)
{
isc_throw
(
RFCViolation
,
"Server-id option was not expected, but "
<<
"received in "
<<
serverReceivedPacketName
(
ex
.
getQ
uery
()
->
getType
()));
<<
serverReceivedPacketName
(
q
uery
->
getType
()));
}
break
;
...
...
@@ -1914,7 +1923,7 @@ Dhcpv4Srv::sanityCheck(const Dhcpv4Exchange& ex, RequirementLevel serverid) {
if
(
!
server_id
)
{
isc_throw
(
RFCViolation
,
"Server-id option was expected, but not "
" received in message "
<<
serverReceivedPacketName
(
ex
.
getQ
uery
()
->
getType
()));
<<
serverReceivedPacketName
(
q
uery
->
getType
()));
}
break
;
...
...
@@ -1924,19 +1933,19 @@ Dhcpv4Srv::sanityCheck(const Dhcpv4Exchange& ex, RequirementLevel serverid) {
}
// If there is HWAddress set and it is non-empty, then we're good
if
(
ex
.
getQ
uery
()
->
getHWAddr
()
&&
!
ex
.
getQ
uery
()
->
getHWAddr
()
->
hwaddr_
.
empty
())
{
if
(
q
uery
->
getHWAddr
()
&&
!
q
uery
->
getHWAddr
()
->
hwaddr_
.
empty
())
{
return
;
}
// There has to be something to uniquely identify the client:
// either non-zero MAC address or client-id option present (or both)
OptionPtr
client_id
=
ex
.
getQ
uery
()
->
getOption
(
DHO_DHCP_CLIENT_IDENTIFIER
);
OptionPtr
client_id
=
q
uery
->
getOption
(
DHO_DHCP_CLIENT_IDENTIFIER
);
// If there's no client-id (or a useless one is provided, i.e. 0 length)
if
(
!
client_id
||
client_id
->
len
()
==
client_id
->
getHeaderLen
())
{
isc_throw
(
RFCViolation
,
"Missing or useless client-id and no HW address "
" provided in message "
<<
serverReceivedPacketName
(
ex
.
getQ
uery
()
->
getType
()));
<<
serverReceivedPacketName
(
q
uery
->
getType
()));
}
}
...
...
@@ -2086,10 +2095,15 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
}
}
bool
Dhcpv4Srv
::
classSpecificProcessing
(
const
Pkt4Ptr
&
query
,
const
Pkt4Ptr
&
rsp
)
{
bool
Dhcpv4Srv
::
classSpecificProcessing
(
const
Dhcpv4Exchange
&
ex
)
{
Subnet4Ptr
subnet
=
Dhcpv4Exchange
::
selectSubnet
(
query
);
if
(
!
subnet
)
{
Subnet4Ptr
subnet
=
ex
.
getContext
()
->
subnet_
;
Pkt4Ptr
query
=
ex
.
getQuery
();
Pkt4Ptr
rsp
=
ex
.
getResponse
();
// If any of those is missing, there is nothing to do.
if
(
!
subnet
||
!
query
||
!
rsp
)
{
return
(
true
);
}
...
...
src/bin/dhcp4/dhcp4_srv.h
View file @
5ed8d736
...
...
@@ -75,7 +75,9 @@ public:
/// @param alloc_engine Pointer to the instance of the Allocation Engine
/// used by the server.
/// @param query Pointer to the client message.
Dhcpv4Exchange
(
const
AllocEnginePtr
&
alloc_engine
,
const
Pkt4Ptr
&
query
);
/// @param subnet Pointer to the subnet to which the client belongs.
Dhcpv4Exchange
(
const
AllocEnginePtr
&
alloc_engine
,
const
Pkt4Ptr
&
query
,
const
Subnet4Ptr
&
subnet
);
/// @brief Initializes the instance of the response message.
///
...
...
@@ -85,25 +87,6 @@ public:
/// response is not initialized.
void
initResponse
();
/// @brief Selects the subnet for the message processing.
///
/// The pointer to the selected subnet is stored in the @c ClientContext4
/// structure.
void
selectSubnet
();
/// @brief Selects the subnet for the message processing.
///
/// @todo This variant of the @c selectSubnet method is static and public so
/// as it may be invoked by the @c Dhcpv4Srv object. This is temporary solution
/// and the function will go away once the server code fully supports the use
/// of this class and it obtains the subnet from the context returned by the
/// @c getContext method.
///
/// @param query Pointer to the client's message.
/// @return Pointer to the selected subnet or NULL if no suitable subnet
/// has been found.
static
Subnet4Ptr
selectSubnet
(
const
Pkt4Ptr
&
query
);
/// @brief Returns the pointer to the query from the client.
Pkt4Ptr
getQuery
()
const
{
return
(
query_
);
...
...
@@ -372,10 +355,10 @@ protected:
/// Checks if mandatory option is really there, that forbidden option
/// is not there, and that client-id or server-id appears only once.
///
/// @param
ex DHCPv4 exchange holding
the client's message
to be checked
.
/// @param
query Pointer to
the client's message.
/// @param serverid expectation regarding server-id option
/// @throw RFCViolation if any issues are detected
static
void
sanityCheck
(
const
Dhcpv4Exchange
&
ex
,
RequirementLevel
serverid
);
static
void
sanityCheck
(
const
Pkt4Ptr
&
query
,
RequirementLevel
serverid
);
/// @brief Processes incoming DISCOVER and returns response.
///
...
...
@@ -709,9 +692,19 @@ protected:
/// @brief Selects a subnet for a given client's packet.
///
/// @param question client's message
/// The @c run_hooks parameters controls whether the method should run
/// installed hooks for subnet selection. Disabling it is useful in
/// cases when the server should sanity check the client's packet before
/// the actual processing. If the sanity check fails, the packet can
/// be discarded.
///
/// @param query client's message
/// @param run_hooks A boolean value which specifies if the method should
/// run installed hooks after selecting the subnet (if true). The default
/// value is true.
/// @return selected subnet (or NULL if no suitable subnet was found)
static
isc
::
dhcp
::
Subnet4Ptr
selectSubnet
(
const
Pkt4Ptr
&
question
);
isc
::
dhcp
::
Subnet4Ptr
selectSubnet
(
const
Pkt4Ptr
&
query
,
const
bool
run_hooks
=
true
)
const
;
/// indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
...
...
@@ -753,12 +746,15 @@ protected:
/// @brief Performs packet processing specific to a class
///
/// This processing is a likely candidate to be pushed into hooks.
/// If the selected subnet, query or response in the @c ex object is NULL
/// this method returns immediately and returns true.
///
/// @param query incoming client's packet
/// @param rsp server's response
/// @note This processing is a likely candidate to be pushed into hooks.
///
/// @param ex The exchange holding both the client's message and the
/// server's response.
/// @return true if successful, false otherwise (will prevent sending response)
bool
classSpecificProcessing
(
const
Pkt4Ptr
&
query
,
const
Pkt4Ptr
&
rsp
);
bool
classSpecificProcessing
(
const
Dhcpv4Exchange
&
ex
);
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
...
...
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
View file @
5ed8d736
...
...
@@ -960,28 +960,25 @@ TEST_F(Dhcpv4SrvTest, sanityCheck) {
pkt
->
setHWAddr
(
generateHWAddr
(
6
));
// Server-id is optional for information-request, so
EXPECT_NO_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
createExchange
(
pkt
),
Dhcpv4Srv
::
OPTIONAL
));
EXPECT_NO_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
pkt
,
Dhcpv4Srv
::
OPTIONAL
));
// Empty packet, no server-id
EXPECT_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
createExchange
(
pkt
)
,
Dhcpv4Srv
::
MANDATORY
),
EXPECT_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
pkt
,
Dhcpv4Srv
::
MANDATORY
),
RFCViolation
);
pkt
->
addOption
(
srv
->
getServerID
());
// Server-id is mandatory and present = no exception
EXPECT_NO_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
createExchange
(
pkt
),
Dhcpv4Srv
::
MANDATORY
));
EXPECT_NO_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
pkt
,
Dhcpv4Srv
::
MANDATORY
));
// Server-id is forbidden, but present => exception
EXPECT_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
createExchange
(
pkt
)
,
Dhcpv4Srv
::
FORBIDDEN
),
EXPECT_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
pkt
,
Dhcpv4Srv
::
FORBIDDEN
),
RFCViolation
);
// There's no client-id and no HWADDR. Server needs something to
// identify the client
pkt
->
setHWAddr
(
generateHWAddr
(
0
));
EXPECT_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
createExchange
(
pkt
),
Dhcpv4Srv
::
MANDATORY
),
EXPECT_THROW
(
NakedDhcpv4Srv
::
sanityCheck
(
pkt
,
Dhcpv4Srv
::
MANDATORY
),
RFCViolation
);
}
...
...
src/bin/dhcp4/tests/dhcp4_test_utils.cc
View file @
5ed8d736
...
...
@@ -589,7 +589,7 @@ Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv,
Dhcpv4Exchange
Dhcpv4SrvTest
::
createExchange
(
const
Pkt4Ptr
&
query
)
{
return
(
Dhcpv4Exchange
(
srv_
.
alloc_engine_
,
query
));
return
(
Dhcpv4Exchange
(
srv_
.
alloc_engine_
,
query
,
srv_
.
selectSubnet
(
query
)
));
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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