|
@@ -104,9 +104,6 @@ namespace {
|
|
// DHCPv6 Client FQDN Option sent by a client. They will be removed
|
|
// DHCPv6 Client FQDN Option sent by a client. They will be removed
|
|
// when DDNS parameters for DHCPv6 are implemented with the ticket #3034.
|
|
// when DDNS parameters for DHCPv6 are implemented with the ticket #3034.
|
|
|
|
|
|
-// Should server always include the FQDN option in its response, regardless
|
|
|
|
-// if it has been requested in ORO (Disabled).
|
|
|
|
-const bool FQDN_ALWAYS_INCLUDE = false;
|
|
|
|
// Enable AAAA RR update delegation to the client (Disabled).
|
|
// Enable AAAA RR update delegation to the client (Disabled).
|
|
const bool FQDN_ALLOW_CLIENT_UPDATE = false;
|
|
const bool FQDN_ALLOW_CLIENT_UPDATE = false;
|
|
// Globally enable updates (Enabled).
|
|
// Globally enable updates (Enabled).
|
|
@@ -211,6 +208,33 @@ void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
|
|
IfaceMgr::instance().send(packet);
|
|
IfaceMgr::instance().send(packet);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+bool
|
|
|
|
+Dhcpv6Srv::testServerID(const Pkt6Ptr& pkt){
|
|
|
|
+ /// @todo Currently we always check server identifier regardless if
|
|
|
|
+ /// it is allowed in the received message or not (per RFC3315).
|
|
|
|
+ /// If the server identifier is not allowed in the message, the
|
|
|
|
+ /// sanityCheck function should deal with it. We may rethink this
|
|
|
|
+ /// design if we decide that it is appropriate to check at this stage
|
|
|
|
+ /// of message processing that the server identifier must or must not
|
|
|
|
+ /// be present. In such case however, the logic checking server id
|
|
|
|
+ /// will have to be removed from sanityCheck and placed here instead,
|
|
|
|
+ /// to avoid duplicate checks.
|
|
|
|
+ OptionPtr server_id = pkt->getOption(D6O_SERVERID);
|
|
|
|
+ if (server_id){
|
|
|
|
+ // Let us test received ServerID if it is same as ServerID
|
|
|
|
+ // which is beeing used by server
|
|
|
|
+ if (getServerID()->getData() != server_id->getData()){
|
|
|
|
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_PACKET_MISMATCH_SERVERID_DROP)
|
|
|
|
+ .arg(pkt->getName())
|
|
|
|
+ .arg(pkt->getTransid())
|
|
|
|
+ .arg(pkt->getIface());
|
|
|
|
+ return (false);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // retun True if: no serverid received or ServerIDs matching
|
|
|
|
+ return (true);
|
|
|
|
+}
|
|
|
|
+
|
|
bool Dhcpv6Srv::run() {
|
|
bool Dhcpv6Srv::run() {
|
|
while (!shutdown_) {
|
|
while (!shutdown_) {
|
|
/// @todo Calculate actual timeout to the next event (e.g. lease
|
|
/// @todo Calculate actual timeout to the next event (e.g. lease
|
|
@@ -283,6 +307,12 @@ bool Dhcpv6Srv::run() {
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ // Check if received query carries server identifier matching
|
|
|
|
+ // server identifier being used by the server.
|
|
|
|
+ if (!testServerID(query)){
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
|
|
.arg(query->getName());
|
|
.arg(query->getName());
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
|
|
@@ -316,6 +346,9 @@ bool Dhcpv6Srv::run() {
|
|
callout_handle->getArgument("query6", query);
|
|
callout_handle->getArgument("query6", query);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Assign this packet to a class, if possible
|
|
|
|
+ classifyPacket(query);
|
|
|
|
+
|
|
try {
|
|
try {
|
|
NameChangeRequestPtr ncr;
|
|
NameChangeRequestPtr ncr;
|
|
switch (query->getType()) {
|
|
switch (query->getType()) {
|
|
@@ -610,10 +643,11 @@ Dhcpv6Srv::generateServerID() {
|
|
seconds -= DUID_TIME_EPOCH;
|
|
seconds -= DUID_TIME_EPOCH;
|
|
|
|
|
|
OptionBuffer srvid(8 + iface->getMacLen());
|
|
OptionBuffer srvid(8 + iface->getMacLen());
|
|
- writeUint16(DUID::DUID_LLT, &srvid[0]);
|
|
+ // We know that the buffer is more than 8 bytes long at this point.
|
|
- writeUint16(HWTYPE_ETHERNET, &srvid[2]);
|
|
+ writeUint16(DUID::DUID_LLT, &srvid[0], 2);
|
|
- writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
|
|
+ writeUint16(HWTYPE_ETHERNET, &srvid[2], 2);
|
|
- memcpy(&srvid[0] + 8, iface->getMac(), iface->getMacLen());
|
|
+ writeUint32(static_cast<uint32_t>(seconds), &srvid[4], 4);
|
|
|
|
+ memcpy(&srvid[8], iface->getMac(), iface->getMacLen());
|
|
|
|
|
|
serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
|
|
serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
|
|
srvid.begin(), srvid.end()));
|
|
srvid.begin(), srvid.end()));
|
|
@@ -626,8 +660,8 @@ Dhcpv6Srv::generateServerID() {
|
|
// See Section 9.3 of RFC3315 for details.
|
|
// See Section 9.3 of RFC3315 for details.
|
|
|
|
|
|
OptionBuffer srvid(12);
|
|
OptionBuffer srvid(12);
|
|
- writeUint16(DUID::DUID_EN, &srvid[0]);
|
|
+ writeUint16(DUID::DUID_EN, &srvid[0], srvid.size());
|
|
- writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
|
|
+ writeUint32(ENTERPRISE_ID_ISC, &srvid[2], srvid.size() - 2);
|
|
|
|
|
|
// Length of the identifier is company specific. I hereby declare
|
|
// Length of the identifier is company specific. I hereby declare
|
|
// ISC "standard" of 6 bytes long pseudo-random numbers.
|
|
// ISC "standard" of 6 bytes long pseudo-random numbers.
|
|
@@ -887,8 +921,7 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
void
|
|
-Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
|
|
+Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
|
|
- const Option6ClientFqdnPtr& fqdn) {
|
|
|
|
|
|
|
|
// We need to allocate addresses for all IA_NA options in the client's
|
|
// We need to allocate addresses for all IA_NA options in the client's
|
|
// question (i.e. SOLICIT or REQUEST) message.
|
|
// question (i.e. SOLICIT or REQUEST) message.
|
|
@@ -941,10 +974,9 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
|
|
opt != question->options_.end(); ++opt) {
|
|
opt != question->options_.end(); ++opt) {
|
|
switch (opt->second->getType()) {
|
|
switch (opt->second->getType()) {
|
|
case D6O_IA_NA: {
|
|
case D6O_IA_NA: {
|
|
- OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
|
|
+ OptionPtr answer_opt = assignIA_NA(subnet, duid, question, answer,
|
|
boost::dynamic_pointer_cast<
|
|
boost::dynamic_pointer_cast<
|
|
- Option6IA>(opt->second),
|
|
+ Option6IA>(opt->second));
|
|
- fqdn);
|
|
|
|
if (answer_opt) {
|
|
if (answer_opt) {
|
|
answer->addOption(answer_opt);
|
|
answer->addOption(answer_opt);
|
|
}
|
|
}
|
|
@@ -964,14 +996,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-Option6ClientFqdnPtr
|
|
+void
|
|
-Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
|
|
+Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer) {
|
|
// Get Client FQDN Option from the client's message. If this option hasn't
|
|
// Get Client FQDN Option from the client's message. If this option hasn't
|
|
// been included, do nothing.
|
|
// been included, do nothing.
|
|
Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
|
|
Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
|
|
Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
|
|
Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
|
|
if (!fqdn) {
|
|
if (!fqdn) {
|
|
- return (fqdn);
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
|
@@ -1022,13 +1054,14 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
|
|
// generate one.
|
|
// generate one.
|
|
if (fqdn->getDomainNameType() == Option6ClientFqdn::PARTIAL) {
|
|
if (fqdn->getDomainNameType() == Option6ClientFqdn::PARTIAL) {
|
|
std::ostringstream name;
|
|
std::ostringstream name;
|
|
- if (fqdn->getDomainName().empty()) {
|
|
+ if (fqdn->getDomainName().empty() || FQDN_REPLACE_CLIENT_NAME) {
|
|
- name << FQDN_GENERATED_PARTIAL_NAME;
|
|
+ fqdn->setDomainName("", Option6ClientFqdn::PARTIAL);
|
|
|
|
+
|
|
} else {
|
|
} else {
|
|
name << fqdn->getDomainName();
|
|
name << fqdn->getDomainName();
|
|
|
|
+ name << "." << FQDN_PARTIAL_SUFFIX;
|
|
|
|
+ fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
|
|
}
|
|
}
|
|
- name << "." << FQDN_PARTIAL_SUFFIX;
|
|
|
|
- fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
|
|
|
|
|
|
|
|
// Server may be configured to replace a name supplied by a client,
|
|
// Server may be configured to replace a name supplied by a client,
|
|
// even if client supplied fully qualified domain-name.
|
|
// even if client supplied fully qualified domain-name.
|
|
@@ -1039,58 +1072,17 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // Return the FQDN option which can be included in the server's response.
|
|
+ // The FQDN has been processed successfully. Let's append it to the
|
|
- // Note that it doesn't have to be included, if client didn't request
|
|
+ // response to be sent to a client. Note that the Client FQDN option is
|
|
- // it using ORO and server is not configured to always include it.
|
|
+ // always sent back to the client if Client FQDN was included in the
|
|
- return (fqdn_resp);
|
|
+ // client's message.
|
|
|
|
+ answer->addOption(fqdn_resp);
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
void
|
|
void
|
|
-Dhcpv6Srv::appendClientFqdn(const Pkt6Ptr& question,
|
|
+Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer) {
|
|
- Pkt6Ptr& answer,
|
|
+ // Don't create NameChangeRequests if DNS updates are disabled.
|
|
- const Option6ClientFqdnPtr& fqdn) {
|
|
+ if (!FQDN_ENABLE_UPDATE) {
|
|
-
|
|
|
|
- // If FQDN is NULL, it means that client did not request DNS Update, plus
|
|
|
|
- // server doesn't force updates.
|
|
|
|
- if (!fqdn) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Server sends back the FQDN option to the client if client has requested
|
|
|
|
- // it using Option Request Option. However, server may be configured to
|
|
|
|
- // send the FQDN option in its response, regardless whether client requested
|
|
|
|
- // it or not.
|
|
|
|
- bool include_fqdn = FQDN_ALWAYS_INCLUDE;
|
|
|
|
- if (!include_fqdn) {
|
|
|
|
- OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast<
|
|
|
|
- OptionUint16Array>(question->getOption(D6O_ORO));
|
|
|
|
- if (oro) {
|
|
|
|
- const std::vector<uint16_t>& values = oro->getValues();
|
|
|
|
- for (int i = 0; i < values.size(); ++i) {
|
|
|
|
- if (values[i] == D6O_CLIENT_FQDN) {
|
|
|
|
- include_fqdn = true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (include_fqdn) {
|
|
|
|
- LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
|
|
|
- DHCP6_DDNS_SEND_FQDN).arg(fqdn->toText());
|
|
|
|
- answer->addOption(fqdn);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void
|
|
|
|
-Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
|
|
|
|
- const Option6ClientFqdnPtr& opt_fqdn) {
|
|
|
|
-
|
|
|
|
- // It is likely that client haven't included the FQDN option in the message
|
|
|
|
- // and server is not configured to always update DNS. In such cases,
|
|
|
|
- // FQDN option will be NULL. This is valid state, so we simply return.
|
|
|
|
- if (!opt_fqdn) {
|
|
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1103,6 +1095,14 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
|
|
<< " NULL when creating DNS NameChangeRequest");
|
|
<< " NULL when creating DNS NameChangeRequest");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // It is likely that client haven't included the FQDN option. In such case,
|
|
|
|
+ // FQDN option will be NULL. This is valid state, so we simply return.
|
|
|
|
+ Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
|
|
|
|
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
|
|
|
|
+ if (!opt_fqdn) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
// Get the Client Id. It is mandatory and a function creating a response
|
|
// Get the Client Id. It is mandatory and a function creating a response
|
|
// would have thrown an exception if it was missing. Thus throwning
|
|
// would have thrown an exception if it was missing. Thus throwning
|
|
// Unexpected if it is missing as it is a programming error.
|
|
// Unexpected if it is missing as it is a programming error.
|
|
@@ -1130,8 +1130,8 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
|
|
OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
|
|
OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
|
|
for (OptionCollection::const_iterator answer_ia =
|
|
for (OptionCollection::const_iterator answer_ia =
|
|
answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
|
|
answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
|
|
- // @todo IA_NA may contain multiple addresses. We should process
|
|
+ /// @todo IA_NA may contain multiple addresses. We should process
|
|
- // each address individually. Currently we get only one.
|
|
+ /// each address individually. Currently we get only one.
|
|
Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
|
|
Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
|
|
Option6IAAddr>(answer_ia->second->getOption(D6O_IAADDR));
|
|
Option6IAAddr>(answer_ia->second->getOption(D6O_IAADDR));
|
|
// We need an address to create a name-to-address mapping.
|
|
// We need an address to create a name-to-address mapping.
|
|
@@ -1157,31 +1157,33 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
|
|
|
|
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
|
DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr.toText());
|
|
DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr.toText());
|
|
|
|
+
|
|
|
|
+ /// @todo Currently we create NCR with the first IPv6 address that
|
|
|
|
+ /// is carried in one of the IA_NAs. In the future, the NCR API should
|
|
|
|
+ /// be extended to map multiple IPv6 addresses to a single FQDN.
|
|
|
|
+ /// In such case, this return statement will be removed.
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
void
|
|
Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
|
|
Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
|
|
- // If we haven't performed a DNS Update when lease was acquired,
|
|
+ // Don't create NameChangeRequests if DNS updates are disabled.
|
|
- // there is nothing to do here.
|
|
+ if (!FQDN_ENABLE_UPDATE) {
|
|
- if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
|
|
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- // When lease was added into a database the host name should have
|
|
+ // If we haven't performed a DNS Update when lease was acquired,
|
|
- // been added. The hostname can be empty if someone messed up in the
|
|
+ // there is nothing to do here.
|
|
- // lease data base and removed the hostname.
|
|
+ if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
|
|
- if (lease->hostname_.empty()) {
|
|
|
|
- LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_EMPTY_HOSTNAME)
|
|
|
|
- .arg(lease->addr_.toText());
|
|
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// If hostname is non-empty, try to convert it to wire format so as
|
|
// If hostname is non-empty, try to convert it to wire format so as
|
|
// DHCID can be computed from it. This may throw an exception if hostname
|
|
// DHCID can be computed from it. This may throw an exception if hostname
|
|
- // has invalid format. Again, this should be only possible in case of
|
|
+ // has invalid format or is empty. Again, this should be only possible
|
|
- // manual intervention in the database. Note that the last parameter
|
|
+ // in case of manual intervention in the database. Note that the last
|
|
- // passed to the writeFqdn function forces conversion of the FQDN
|
|
+ // parameter passed to the writeFqdn function forces conversion of the FQDN
|
|
// to lower case. This is required by the RFC4701, section 3.5.
|
|
// to lower case. This is required by the RFC4701, section 3.5.
|
|
// The DHCID computation is further in this function.
|
|
// The DHCID computation is further in this function.
|
|
std::vector<uint8_t> hostname_wire;
|
|
std::vector<uint8_t> hostname_wire;
|
|
@@ -1189,7 +1191,8 @@ Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
|
|
OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
|
|
OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
|
|
} catch (const Exception& ex) {
|
|
} catch (const Exception& ex) {
|
|
LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_INVALID_HOSTNAME)
|
|
LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_INVALID_HOSTNAME)
|
|
- .arg(lease->hostname_);
|
|
+ .arg(lease->hostname_.empty() ? "(empty)" : lease->hostname_)
|
|
|
|
+ .arg(lease->addr_.toText());
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1199,7 +1202,7 @@ Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
|
|
if (!lease->duid_) {
|
|
if (!lease->duid_) {
|
|
isc_throw(isc::Unexpected, "DUID must be set when creating"
|
|
isc_throw(isc::Unexpected, "DUID must be set when creating"
|
|
<< " NameChangeRequest for DNS records removal for "
|
|
<< " NameChangeRequest for DNS records removal for "
|
|
- << lease->addr_.toText());
|
|
+ << lease->addr_);
|
|
|
|
|
|
}
|
|
}
|
|
isc::dhcp_ddns::D2Dhcid dhcid(*lease->duid_, hostname_wire);
|
|
isc::dhcp_ddns::D2Dhcid dhcid(*lease->duid_, hostname_wire);
|
|
@@ -1230,14 +1233,14 @@ Dhcpv6Srv::sendNameChangeRequests() {
|
|
|
|
|
|
OptionPtr
|
|
OptionPtr
|
|
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
- const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
|
|
+ const Pkt6Ptr& query, const Pkt6Ptr& answer,
|
|
- const Option6ClientFqdnPtr& fqdn) {
|
|
+ boost::shared_ptr<Option6IA> ia) {
|
|
// If there is no subnet selected for handling this IA_NA, the only thing to do left is
|
|
// If there is no subnet selected for handling this IA_NA, the only thing to do left is
|
|
// to say that we are sorry, but the user won't get an address. As a convenience, we
|
|
// to say that we are sorry, but the user won't get an address. As a convenience, we
|
|
// use a different status text to indicate that (compare to the same status code,
|
|
// use a different status text to indicate that (compare to the same status code,
|
|
// but different wording below)
|
|
// but different wording below)
|
|
if (!subnet) {
|
|
if (!subnet) {
|
|
- // Create empty IA_NA option with IAID matching the request.
|
|
+ // Creatasse empty IA_NA option with IAID matching the request.
|
|
// Note that we don't use OptionDefinition class to create this option.
|
|
// Note that we don't use OptionDefinition class to create this option.
|
|
// This is because we prefer using a constructor of Option6IA that
|
|
// This is because we prefer using a constructor of Option6IA that
|
|
// initializes IAID. Otherwise we would have to use setIAID() after
|
|
// initializes IAID. Otherwise we would have to use setIAID() after
|
|
@@ -1252,16 +1255,16 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
// Check if the client sent us a hint in his IA_NA. Clients may send an
|
|
// Check if the client sent us a hint in his IA_NA. Clients may send an
|
|
// address in their IA_NA options as a suggestion (e.g. the last address
|
|
// address in their IA_NA options as a suggestion (e.g. the last address
|
|
// they used before).
|
|
// they used before).
|
|
- boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>
|
|
+ boost::shared_ptr<Option6IAAddr> hint_opt =
|
|
- (ia->getOption(D6O_IAADDR));
|
|
+ boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
|
|
IOAddress hint("::");
|
|
IOAddress hint("::");
|
|
- if (hintOpt) {
|
|
+ if (hint_opt) {
|
|
- hint = hintOpt->getAddress();
|
|
+ hint = hint_opt->getAddress();
|
|
}
|
|
}
|
|
|
|
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
|
|
- .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
|
|
+ .arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID())
|
|
- .arg(hintOpt?hint.toText():"(no hint)");
|
|
+ .arg(hint_opt ? hint.toText() : "(no hint)");
|
|
|
|
|
|
// "Fake" allocation is processing of SOLICIT message. We pretend to do an
|
|
// "Fake" allocation is processing of SOLICIT message. We pretend to do an
|
|
// allocation, but we do not put the lease in the database. That is ok,
|
|
// allocation, but we do not put the lease in the database. That is ok,
|
|
@@ -1285,6 +1288,8 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
// the update.
|
|
// the update.
|
|
bool do_fwd = false;
|
|
bool do_fwd = false;
|
|
bool do_rev = false;
|
|
bool do_rev = false;
|
|
|
|
+ Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
|
|
|
|
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
|
|
if (fqdn) {
|
|
if (fqdn) {
|
|
// Flag S must not coexist with flag N being set to 1, so if S=1
|
|
// Flag S must not coexist with flag N being set to 1, so if S=1
|
|
// server takes responsibility for both reverse and forward updates.
|
|
// server takes responsibility for both reverse and forward updates.
|
|
@@ -1306,13 +1311,15 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
// will try to honour the hint, but it is just a hint - some other address
|
|
// will try to honour the hint, but it is just a hint - some other address
|
|
// may be used instead. If fake_allocation is set to false, the lease will
|
|
// may be used instead. If fake_allocation is set to false, the lease will
|
|
// be inserted into the LeaseMgr as well.
|
|
// be inserted into the LeaseMgr as well.
|
|
|
|
+ Lease6Collection old_leases;
|
|
Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
|
|
Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
|
|
ia->getIAID(),
|
|
ia->getIAID(),
|
|
hint, Lease::TYPE_NA,
|
|
hint, Lease::TYPE_NA,
|
|
do_fwd, do_rev,
|
|
do_fwd, do_rev,
|
|
hostname,
|
|
hostname,
|
|
fake_allocation,
|
|
fake_allocation,
|
|
- callout_handle);
|
|
+ callout_handle,
|
|
|
|
+ old_leases);
|
|
/// @todo: Handle more than one lease
|
|
/// @todo: Handle more than one lease
|
|
Lease6Ptr lease;
|
|
Lease6Ptr lease;
|
|
if (!leases.empty()) {
|
|
if (!leases.empty()) {
|
|
@@ -1347,26 +1354,25 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
// but this is considered waste of bandwidth as absence of status
|
|
// but this is considered waste of bandwidth as absence of status
|
|
// code is considered a success.
|
|
// code is considered a success.
|
|
|
|
|
|
|
|
+ Lease6Ptr old_lease;
|
|
|
|
+ if (!old_leases.empty()) {
|
|
|
|
+ old_lease = *old_leases.begin();
|
|
|
|
+ }
|
|
// Allocation engine may have returned an existing lease. If so, we
|
|
// Allocation engine may have returned an existing lease. If so, we
|
|
// have to check that the FQDN settings we provided are the same
|
|
// have to check that the FQDN settings we provided are the same
|
|
// that were set. If they aren't, we will have to remove existing
|
|
// that were set. If they aren't, we will have to remove existing
|
|
// DNS records and update the lease with the new settings.
|
|
// DNS records and update the lease with the new settings.
|
|
- if ((lease->hostname_ != hostname) || (lease->fqdn_fwd_ != do_fwd) ||
|
|
+ if (!fake_allocation && old_lease &&
|
|
- (lease->fqdn_rev_ != do_rev)) {
|
|
+ !lease->hasIdenticalFqdn(*old_lease)) {
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
|
|
DHCP6_DDNS_LEASE_ASSIGN_FQDN_CHANGE)
|
|
DHCP6_DDNS_LEASE_ASSIGN_FQDN_CHANGE)
|
|
- .arg(lease->toText())
|
|
+ .arg(old_lease->toText())
|
|
.arg(hostname)
|
|
.arg(hostname)
|
|
.arg(do_rev ? "true" : "false")
|
|
.arg(do_rev ? "true" : "false")
|
|
.arg(do_fwd ? "true" : "false");
|
|
.arg(do_fwd ? "true" : "false");
|
|
|
|
|
|
// Schedule removal of the existing lease.
|
|
// Schedule removal of the existing lease.
|
|
- createRemovalNameChangeRequest(lease);
|
|
+ createRemovalNameChangeRequest(old_lease);
|
|
- // Set the new lease properties and update.
|
|
|
|
- lease->hostname_ = hostname;
|
|
|
|
- lease->fqdn_fwd_ = do_fwd;
|
|
|
|
- lease->fqdn_rev_ = do_rev;
|
|
|
|
- LeaseMgrFactory::instance().updateLease6(lease);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
} else {
|
|
@@ -1434,13 +1440,15 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
// will try to honour the hint, but it is just a hint - some other address
|
|
// will try to honour the hint, but it is just a hint - some other address
|
|
// may be used instead. If fake_allocation is set to false, the lease will
|
|
// may be used instead. If fake_allocation is set to false, the lease will
|
|
// be inserted into the LeaseMgr as well.
|
|
// be inserted into the LeaseMgr as well.
|
|
|
|
+ Lease6Collection old_leases;
|
|
Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
|
|
Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
|
|
- ia->getIAID(),
|
|
+ ia->getIAID(),
|
|
- hint, Lease::TYPE_PD,
|
|
+ hint, Lease::TYPE_PD,
|
|
- false, false,
|
|
+ false, false,
|
|
- string(),
|
|
+ string(),
|
|
- fake_allocation,
|
|
+ fake_allocation,
|
|
- callout_handle);
|
|
+ callout_handle,
|
|
|
|
+ old_leases);
|
|
|
|
|
|
if (!leases.empty()) {
|
|
if (!leases.empty()) {
|
|
|
|
|
|
@@ -1488,8 +1496,8 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
|
|
|
|
OptionPtr
|
|
OptionPtr
|
|
Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
- const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
|
|
+ const Pkt6Ptr& query, const Pkt6Ptr& answer,
|
|
- const Option6ClientFqdnPtr& fqdn) {
|
|
+ boost::shared_ptr<Option6IA> ia) {
|
|
if (!subnet) {
|
|
if (!subnet) {
|
|
// There's no subnet select for this client. There's nothing to renew.
|
|
// There's no subnet select for this client. There's nothing to renew.
|
|
boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
|
|
boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
|
|
@@ -1538,6 +1546,8 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
// the update.
|
|
// the update.
|
|
bool do_fwd = false;
|
|
bool do_fwd = false;
|
|
bool do_rev = false;
|
|
bool do_rev = false;
|
|
|
|
+ Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
|
|
|
|
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
|
|
if (fqdn) {
|
|
if (fqdn) {
|
|
if (fqdn->getFlag(Option6ClientFqdn::FLAG_S)) {
|
|
if (fqdn->getFlag(Option6ClientFqdn::FLAG_S)) {
|
|
do_fwd = true;
|
|
do_fwd = true;
|
|
@@ -1730,8 +1740,7 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
void
|
|
-Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
|
|
+Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
|
|
- const Option6ClientFqdnPtr& fqdn) {
|
|
|
|
|
|
|
|
// We need to renew addresses for all IA_NA options in the client's
|
|
// We need to renew addresses for all IA_NA options in the client's
|
|
// RENEW message.
|
|
// RENEW message.
|
|
@@ -1775,10 +1784,9 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
|
|
switch (opt->second->getType()) {
|
|
switch (opt->second->getType()) {
|
|
|
|
|
|
case D6O_IA_NA: {
|
|
case D6O_IA_NA: {
|
|
- OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
|
|
+ OptionPtr answer_opt = renewIA_NA(subnet, duid, renew, reply,
|
|
boost::dynamic_pointer_cast<
|
|
boost::dynamic_pointer_cast<
|
|
- Option6IA>(opt->second),
|
|
+ Option6IA>(opt->second));
|
|
- fqdn);
|
|
|
|
if (answer_opt) {
|
|
if (answer_opt) {
|
|
reply->addOption(answer_opt);
|
|
reply->addOption(answer_opt);
|
|
}
|
|
}
|
|
@@ -2175,13 +2183,14 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
|
|
appendRequestedOptions(solicit, advertise);
|
|
appendRequestedOptions(solicit, advertise);
|
|
appendRequestedVendorOptions(solicit, advertise);
|
|
appendRequestedVendorOptions(solicit, advertise);
|
|
|
|
|
|
- Option6ClientFqdnPtr fqdn = processClientFqdn(solicit);
|
|
+ processClientFqdn(solicit, advertise);
|
|
- assignLeases(solicit, advertise, fqdn);
|
|
+ assignLeases(solicit, advertise);
|
|
- appendClientFqdn(solicit, advertise, fqdn);
|
|
|
|
// Note, that we don't create NameChangeRequests here because we don't
|
|
// Note, that we don't create NameChangeRequests here because we don't
|
|
// perform DNS Updates for Solicit. Client must send Request to update
|
|
// perform DNS Updates for Solicit. Client must send Request to update
|
|
// DNS.
|
|
// DNS.
|
|
|
|
|
|
|
|
+ generateFqdn(advertise);
|
|
|
|
+
|
|
return (advertise);
|
|
return (advertise);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2197,10 +2206,10 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
|
|
appendRequestedOptions(request, reply);
|
|
appendRequestedOptions(request, reply);
|
|
appendRequestedVendorOptions(request, reply);
|
|
appendRequestedVendorOptions(request, reply);
|
|
|
|
|
|
- Option6ClientFqdnPtr fqdn = processClientFqdn(request);
|
|
+ processClientFqdn(request, reply);
|
|
- assignLeases(request, reply, fqdn);
|
|
+ assignLeases(request, reply);
|
|
- appendClientFqdn(request, reply, fqdn);
|
|
+ generateFqdn(reply);
|
|
- createNameChangeRequests(reply, fqdn);
|
|
+ createNameChangeRequests(reply);
|
|
|
|
|
|
return (reply);
|
|
return (reply);
|
|
}
|
|
}
|
|
@@ -2216,12 +2225,12 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
|
|
appendDefaultOptions(renew, reply);
|
|
appendDefaultOptions(renew, reply);
|
|
appendRequestedOptions(renew, reply);
|
|
appendRequestedOptions(renew, reply);
|
|
|
|
|
|
- Option6ClientFqdnPtr fqdn = processClientFqdn(renew);
|
|
+ processClientFqdn(renew, reply);
|
|
- renewLeases(renew, reply, fqdn);
|
|
+ renewLeases(renew, reply);
|
|
- appendClientFqdn(renew, reply, fqdn);
|
|
+ generateFqdn(reply);
|
|
- createNameChangeRequests(reply, fqdn);
|
|
+ createNameChangeRequests(reply);
|
|
|
|
|
|
- return reply;
|
|
+ return (reply);
|
|
}
|
|
}
|
|
|
|
|
|
Pkt6Ptr
|
|
Pkt6Ptr
|
|
@@ -2352,10 +2361,13 @@ Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
|
|
// The buffer being read comprises a set of options, each starting with
|
|
// The buffer being read comprises a set of options, each starting with
|
|
// a two-byte type code and a two-byte length field.
|
|
// a two-byte type code and a two-byte length field.
|
|
while (offset + 4 <= length) {
|
|
while (offset + 4 <= length) {
|
|
- uint16_t opt_type = isc::util::readUint16(&buf[offset]);
|
|
+ // At this point, from the while condition, we know that there
|
|
|
|
+ // are at least 4 bytes available following offset in the
|
|
|
|
+ // buffer.
|
|
|
|
+ uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
|
|
offset += 2;
|
|
offset += 2;
|
|
|
|
|
|
- uint16_t opt_len = isc::util::readUint16(&buf[offset]);
|
|
+ uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
|
|
offset += 2;
|
|
offset += 2;
|
|
|
|
|
|
if (offset + opt_len > length) {
|
|
if (offset + opt_len > length) {
|
|
@@ -2423,5 +2435,112 @@ Dhcpv6Srv::ifaceMgrSocket6ErrorHandler(const std::string& errmsg) {
|
|
LOG_WARN(dhcp6_logger, DHCP6_OPEN_SOCKET_FAIL).arg(errmsg);
|
|
LOG_WARN(dhcp6_logger, DHCP6_OPEN_SOCKET_FAIL).arg(errmsg);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
|
|
|
|
+
|
|
|
|
+ boost::shared_ptr<OptionCustom> vclass =
|
|
|
|
+ boost::dynamic_pointer_cast<OptionCustom>(pkt->getOption(D6O_VENDOR_CLASS));
|
|
|
|
+
|
|
|
|
+ if (!vclass) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ string classes = "";
|
|
|
|
+
|
|
|
|
+ // DOCSIS specific section
|
|
|
|
+ if (vclass->readString(VENDOR_CLASS_STRING_INDEX)
|
|
|
|
+ .find(DOCSIS3_CLASS_MODEM) != std::string::npos) {
|
|
|
|
+ pkt->addClass(DOCSIS3_CLASS_MODEM);
|
|
|
|
+ classes += string(DOCSIS3_CLASS_MODEM) + " ";
|
|
|
|
+ } else
|
|
|
|
+ if (vclass->readString(VENDOR_CLASS_STRING_INDEX)
|
|
|
|
+ .find(DOCSIS3_CLASS_EROUTER) != std::string::npos) {
|
|
|
|
+ pkt->addClass(DOCSIS3_CLASS_EROUTER);
|
|
|
|
+ classes += string(DOCSIS3_CLASS_EROUTER) + " ";
|
|
|
|
+ }else
|
|
|
|
+ {
|
|
|
|
+ // Otherwise use the string as is
|
|
|
|
+ classes += vclass->readString(VENDOR_CLASS_STRING_INDEX);
|
|
|
|
+ pkt->addClass(vclass->readString(VENDOR_CLASS_STRING_INDEX));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!classes.empty()) {
|
|
|
|
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
|
|
|
|
+ .arg(classes);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
|
|
|
|
+ if (!answer) {
|
|
|
|
+ isc_throw(isc::Unexpected, "an instance of the object encapsulating"
|
|
|
|
+ " a message must not be NULL when generating FQDN");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // It is likely that client haven't included the FQDN option. In such case,
|
|
|
|
+ // FQDN option will be NULL. Also, there is nothing to do if the option
|
|
|
|
+ // is present and conveys the non-empty FQDN.
|
|
|
|
+ Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
|
|
|
|
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
|
|
|
|
+ if (!fqdn || !fqdn->getDomainName().empty()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Get the first IA_NA acquired for the client.
|
|
|
|
+ OptionPtr ia = answer->getOption(D6O_IA_NA);
|
|
|
|
+ if (!ia) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // If it has any IAAddr, use the first one to generate unique FQDN.
|
|
|
|
+ Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
|
|
|
|
+ Option6IAAddr>(ia->getOption(D6O_IAADDR));
|
|
|
|
+ if (!iaaddr) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // Get the IPv6 address acquired by the client.
|
|
|
|
+ IOAddress addr = iaaddr->getAddress();
|
|
|
|
+ std::string hostname = addr.toText();
|
|
|
|
+ // Colons may not be ok for FQDNs so let's replace them with hyphens.
|
|
|
|
+ std::replace(hostname.begin(), hostname.end(), ':', '-');
|
|
|
|
+ std::ostringstream stream;
|
|
|
|
+ // The final FQDN consists of the partial domain name and the suffix.
|
|
|
|
+ // For example, if the acquired address is 2001:db8:1::2, the generated
|
|
|
|
+ // FQDN may be:
|
|
|
|
+ // host-2001-db8:1--2.example.com.
|
|
|
|
+ // where prefix 'host' should be configurable. The domain name suffix
|
|
|
|
+ // should also be configurable.
|
|
|
|
+ stream << "host-" << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
|
|
|
|
+ try {
|
|
|
|
+ // The lease has been acquired but the FQDN for this lease hasn't
|
|
|
|
+ // been updated in the lease database. We now have new FQDN
|
|
|
|
+ // generated, so the lease database has to be updated here.
|
|
|
|
+ // However, never update lease database for Advertise, just send
|
|
|
|
+ // our notion of client's FQDN in the Client FQDN option.
|
|
|
|
+ if (answer->getType() != DHCPV6_ADVERTISE) {
|
|
|
|
+ Lease6Ptr lease =
|
|
|
|
+ LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
|
|
|
|
+ if (lease) {
|
|
|
|
+ lease->hostname_ = stream.str();
|
|
|
|
+ LeaseMgrFactory::instance().updateLease6(lease);
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ isc_throw(isc::Unexpected, "there is no lease in the database "
|
|
|
|
+ " for address " << addr << ", so as it is impossible"
|
|
|
|
+ " to update FQDN data. This is a programmatic error"
|
|
|
|
+ " as the given address is now being handed to the"
|
|
|
|
+ " client");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Set the generated FQDN in the Client FQDN option.
|
|
|
|
+ fqdn->setDomainName(stream.str(), Option6ClientFqdn::FULL);
|
|
|
|
+
|
|
|
|
+ } catch (const Exception& ex) {
|
|
|
|
+ LOG_ERROR(dhcp6_logger, DHCP6_NAME_GEN_UPDATE_FAIL)
|
|
|
|
+ .arg(hostname)
|
|
|
|
+ .arg(ex.what());
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
};
|
|
};
|
|
};
|
|
};
|