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
9dc963b3
Commit
9dc963b3
authored
Sep 09, 2013
by
Marcin Siodelski
Browse files
[3035] Generate client hostname from IP address acquired.
parent
ee888857
Changes
3
Hide whitespace changes
Inline
Side-by-side
src/bin/dhcp4/dhcp4_messages.mes
View file @
9dc963b3
...
...
@@ -139,6 +139,12 @@ specified client after receiving a REQUEST message from it. There are many
possible reasons for such a failure. Additional messages will indicate the
reason.
% DHCP4_NAME_GEN_UPDATE_FAIL failed to update the lease after generating name for a client: %1
This message indicates the failure when trying to update the lease and/or
options in the server's response with the hostname generated by the server
from the acquired address. The message argument indicates the reason for the
failure.
% DHCP4_NCR_CREATION_FAILED failed to generate name change requests for DNS: %1
This message indicates that server was unable to generate so called
NameChangeRequests which should be sent to the b10-dhcp_ddns module to create
...
...
src/bin/dhcp4/dhcp4_srv.cc
View file @
9dc963b3
...
...
@@ -89,9 +89,6 @@ const bool FQDN_ALWAYS_INCLUDE = false;
const
bool
FQDN_ALLOW_CLIENT_UPDATE
=
false
;
// Globally enable updates (Enabled).
const
bool
FQDN_ENABLE_UPDATE
=
true
;
// The partial name generated for the client if empty name has been
// supplied.
const
char
*
FQDN_GENERATED_PARTIAL_NAME
=
"myhost"
;
// Do update, even if client requested no updates with N flag (Disabled).
const
bool
FQDN_OVERRIDE_NO_UPDATE
=
false
;
// Server performs an update when client requested delegation (Enabled).
...
...
@@ -810,19 +807,21 @@ Dhcpv4Srv::processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
if
(
fqdn
->
getDomainNameType
()
==
Option4ClientFqdn
::
PARTIAL
)
{
std
::
ostringstream
name
;
if
(
fqdn
->
getDomainName
().
empty
()
||
FQDN_REPLACE_CLIENT_NAME
)
{
name
<<
FQDN_GENERATED_PARTIAL_NAME
;
fqdn_resp
->
setDomainName
(
""
,
Option4ClientFqdn
::
PARTIAL
);
}
else
{
name
<<
fqdn
->
getDomainName
();
name
<<
"."
<<
FQDN_PARTIAL_SUFFIX
;
fqdn_resp
->
setDomainName
(
name
.
str
(),
Option4ClientFqdn
::
FULL
);
}
name
<<
"."
<<
FQDN_PARTIAL_SUFFIX
;
fqdn_resp
->
setDomainName
(
name
.
str
(),
Option4ClientFqdn
::
FULL
);
// Server may be configured to replace a name supplied by a client, even if
// client supplied fully qualified domain-name.
// client supplied fully qualified domain-name. The empty domain-name is
// is set to indicate that the name must be generated when the new lease
// is acquired.
}
else
if
(
FQDN_REPLACE_CLIENT_NAME
)
{
std
::
ostringstream
name
;
name
<<
FQDN_GENERATED_PARTIAL_NAME
<<
"."
<<
FQDN_PARTIAL_SUFFIX
;
fqdn_resp
->
setDomainName
(
name
.
str
(),
Option4ClientFqdn
::
FULL
);
fqdn_resp
->
setDomainName
(
""
,
Option4ClientFqdn
::
PARTIAL
);
}
// Add FQDN option to the response message. Note that, there may be some
...
...
@@ -866,10 +865,7 @@ Dhcpv4Srv::processHostnameOption(const OptionCustomPtr& opt_hostname,
// If there is only one label, it is a root. We will have to generate
// the whole domain name for the client.
if
(
FQDN_REPLACE_CLIENT_NAME
||
(
label_count
<
2
))
{
std
::
ostringstream
resp_hostname
;
resp_hostname
<<
FQDN_GENERATED_PARTIAL_NAME
<<
"."
<<
FQDN_PARTIAL_SUFFIX
<<
"."
;
opt_hostname_resp
->
writeString
(
resp_hostname
.
str
());
opt_hostname_resp
->
writeString
(
""
);
// If there are two labels, it means that the client has specified
// the unqualified name. We have to concatenate the unqalified name
// with the domain name.
...
...
@@ -1022,6 +1018,7 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
std
::
string
hostname
;
bool
fqdn_fwd
=
false
;
bool
fqdn_rev
=
false
;
OptionCustomPtr
opt_hostname
;
Option4ClientFqdnPtr
fqdn
=
boost
::
dynamic_pointer_cast
<
Option4ClientFqdn
>
(
answer
->
getOption
(
DHO_FQDN
));
if
(
fqdn
)
{
...
...
@@ -1029,8 +1026,8 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
fqdn_fwd
=
fqdn
->
getFlag
(
Option4ClientFqdn
::
FLAG_S
);
fqdn_rev
=
!
fqdn
->
getFlag
(
Option4ClientFqdn
::
FLAG_N
);
}
else
{
OptionCustomPtr
opt_hostname
=
boost
::
dynamic_pointer_cast
<
OptionCustom
>
(
answer
->
getOption
(
DHO_HOST_NAME
));
opt_hostname
=
boost
::
dynamic_pointer_cast
<
OptionCustom
>
(
answer
->
getOption
(
DHO_HOST_NAME
));
if
(
opt_hostname
)
{
hostname
=
opt_hostname
->
readString
();
// @todo It could be configurable what sort of updates the server
...
...
@@ -1064,6 +1061,42 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
answer
->
setYiaddr
(
lease
->
addr_
);
// If there has been Client FQDN or Hostname option sent, but the
// hostname is empty, it means that server is responsible for
// generating the entire hostname for the client. The example of the
// client's name, generated from the IP address is: host-192-0-2-3.
if
((
fqdn
||
opt_hostname
)
&&
lease
->
hostname_
.
empty
())
{
hostname
=
lease
->
addr_
.
toText
();
// Replace dots with hyphens.
std
::
replace
(
hostname
.
begin
(),
hostname
.
end
(),
'.'
,
'-'
);
ostringstream
stream
;
// The partial suffix will need to be replaced with the actual
// domain-name for the client when configuration is implemented.
stream
<<
"host-"
<<
hostname
<<
"."
<<
FQDN_PARTIAL_SUFFIX
<<
"."
;
lease
->
hostname_
=
stream
.
str
();
// The operations below are rather safe, but we want to catch
// any potential exceptions (e.g. invalid lease database backend
// implementation) and log an error.
try
{
// The lease update should be safe, because the lease should
// be already in the database. In most cases the exception
// would be thrown if the lease was missing.
LeaseMgrFactory
::
instance
().
updateLease4
(
lease
);
// The name update in the option should be also safe,
// because the generated name is well formed.
if
(
fqdn
)
{
fqdn
->
setDomainName
(
lease
->
hostname_
,
Option4ClientFqdn
::
FULL
);
}
else
if
(
opt_hostname
)
{
opt_hostname
->
writeString
(
lease
->
hostname_
);
}
}
catch
(
const
Exception
&
ex
)
{
LOG_ERROR
(
dhcp4_logger
,
DHCP4_NAME_GEN_UPDATE_FAIL
)
.
arg
(
ex
.
what
());
}
}
// IP Address Lease time (type 51)
opt
=
OptionPtr
(
new
Option
(
Option
::
V4
,
DHO_DHCP_LEASE_TIME
));
opt
->
setUint32
(
lease
->
valid_lft_
);
...
...
src/bin/dhcp4/tests/fqdn_unittest.cc
View file @
9dc963b3
...
...
@@ -76,6 +76,17 @@ public:
return
(
opt_hostname
);
}
// Generates partial hostname from the address. The format of the
// generated address is: host-A-B-C-D, where A.B.C.D is an IP
// address.
std
::
string
generatedNameFromAddress
(
const
IOAddress
&
addr
)
{
std
::
string
gen_name
=
addr
.
toText
();
std
::
replace
(
gen_name
.
begin
(),
gen_name
.
end
(),
'.'
,
'-'
);
std
::
ostringstream
hostname
;
hostname
<<
"host-"
<<
gen_name
;
return
(
hostname
.
str
());
}
// Get the Client FQDN Option from the given message.
Option4ClientFqdnPtr
getClientFqdnOption
(
const
Pkt4Ptr
&
pkt
)
{
return
(
boost
::
dynamic_pointer_cast
<
...
...
@@ -153,7 +164,9 @@ public:
// Test that server generates the appropriate FQDN option in response to
// client's FQDN option.
void
testProcessFqdn
(
const
Pkt4Ptr
&
query
,
const
uint8_t
exp_flags
,
const
std
::
string
&
exp_domain_name
)
{
const
std
::
string
&
exp_domain_name
,
const
Option4ClientFqdn
::
DomainNameType
exp_domain_type
=
Option4ClientFqdn
::
FULL
)
{
ASSERT_TRUE
(
getClientFqdnOption
(
query
));
Pkt4Ptr
answer
;
...
...
@@ -180,7 +193,7 @@ public:
EXPECT_EQ
(
flag_e
,
fqdn
->
getFlag
(
Option4ClientFqdn
::
FLAG_E
));
EXPECT_EQ
(
exp_domain_name
,
fqdn
->
getDomainName
());
EXPECT_EQ
(
Option4ClientFqdn
::
FULL
,
fqdn
->
getDomainNameType
());
EXPECT_EQ
(
exp_domain_type
,
fqdn
->
getDomainNameType
());
}
...
...
@@ -253,7 +266,11 @@ public:
EXPECT_EQ
(
reverse
,
ncr
.
isReverseChange
());
EXPECT_EQ
(
addr
,
ncr
.
getIpAddress
());
EXPECT_EQ
(
fqdn
,
ncr
.
getFqdn
());
EXPECT_EQ
(
dhcid
,
ncr
.
getDhcid
().
toStr
());
// Compare dhcid if it is not empty. In some cases, the DHCID is
// not known in advance and can't be compared.
if
(
!
dhcid
.
empty
())
{
EXPECT_EQ
(
dhcid
,
ncr
.
getDhcid
().
toStr
());
}
EXPECT_EQ
(
expires
,
ncr
.
getLeaseExpiresOn
());
EXPECT_EQ
(
len
,
ncr
.
getLeaseLength
());
EXPECT_EQ
(
isc
::
dhcp_ddns
::
ST_NEW
,
ncr
.
getStatus
());
...
...
@@ -369,8 +386,10 @@ TEST_F(NameDhcpv4SrvTest, serverUpdateUnqualifiedHostname) {
testProcessHostname
(
query
,
"myhost.example.com."
);
}
// Test that server generates the fully qualified domain name for the client
// if clietn supplies empty domain name.
// Test that server sets empty domain-name in the FQDN option when client
// supplied no domain-name. The domain-name is supposed to be set after the
// lease is acquired. The domain-name is then generated from the IP address
// assigned to a client.
TEST_F
(
NameDhcpv4SrvTest
,
serverUpdateForwardNoNameFqdn
)
{
Pkt4Ptr
query
=
generatePktWithFqdn
(
DHCPREQUEST
,
Option4ClientFqdn
::
FLAG_E
|
...
...
@@ -381,7 +400,7 @@ TEST_F(NameDhcpv4SrvTest, serverUpdateForwardNoNameFqdn) {
testProcessFqdn
(
query
,
Option4ClientFqdn
::
FLAG_E
|
Option4ClientFqdn
::
FLAG_S
,
"
myhost.example.com."
);
"
"
,
Option4ClientFqdn
::
PARTIAL
);
}
...
...
@@ -525,6 +544,52 @@ TEST_F(NameDhcpv4SrvTest, processDiscover) {
EXPECT_TRUE
(
srv_
->
name_change_reqs_
.
empty
());
}
// Test that server generates client's hostname from the IP address assigned
// to it when DHCPv4 Client FQDN option specifies an empty domain-name.
TEST_F
(
NameDhcpv4SrvTest
,
processRequestFqdnEmptyDomainName
)
{
Pkt4Ptr
req
=
generatePktWithFqdn
(
DHCPREQUEST
,
Option4ClientFqdn
::
FLAG_S
|
Option4ClientFqdn
::
FLAG_E
,
""
,
Option4ClientFqdn
::
PARTIAL
,
true
);
Pkt4Ptr
reply
;
ASSERT_NO_THROW
(
reply
=
srv_
->
processRequest
(
req
));
checkResponse
(
reply
,
DHCPACK
,
1234
);
// Verify that there is one NameChangeRequest generated.
ASSERT_EQ
(
1
,
srv_
->
name_change_reqs_
.
size
());
// The hostname is generated from the IP address acquired (yiaddr).
std
::
ostringstream
hostname
;
hostname
<<
generatedNameFromAddress
(
reply
->
getYiaddr
())
<<
".example.com."
;
verifyNameChangeRequest
(
isc
::
dhcp_ddns
::
CHG_ADD
,
true
,
true
,
reply
->
getYiaddr
().
toText
(),
hostname
.
str
(),
""
,
// empty DHCID forces that it is not checked
0
,
subnet_
->
getValid
());
}
// Test that server generates client's hostname from the IP address assigned
// to it when Hostname option carries the top level domain-name.
TEST_F
(
NameDhcpv4SrvTest
,
processRequestEmptyHostname
)
{
Pkt4Ptr
req
=
generatePktWithHostname
(
DHCPREQUEST
,
"."
);
Pkt4Ptr
reply
;
ASSERT_NO_THROW
(
reply
=
srv_
->
processRequest
(
req
));
checkResponse
(
reply
,
DHCPACK
,
1234
);
// Verify that there is one NameChangeRequest generated.
ASSERT_EQ
(
1
,
srv_
->
name_change_reqs_
.
size
());
// The hostname is generated from the IP address acquired (yiaddr).
std
::
ostringstream
hostname
;
hostname
<<
generatedNameFromAddress
(
reply
->
getYiaddr
())
<<
".example.com."
;
verifyNameChangeRequest
(
isc
::
dhcp_ddns
::
CHG_ADD
,
true
,
true
,
reply
->
getYiaddr
().
toText
(),
hostname
.
str
(),
""
,
// empty DHCID forces that it is not checked
0
,
subnet_
->
getValid
());
}
// Test that client may send two requests, each carrying FQDN option with
// a different domain-name. Server should use existing lease for the second
// request but modify the DNS entries for the lease according to the contents
...
...
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