1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <dhcp/option_data_types.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/ncr_generator.h>
#include <stdint.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <util/str.h>

using namespace isc;
using namespace isc::dhcp;
using namespace isc::dhcp_ddns;

namespace {

/// @brief Sends name change request to D2 using lease information.
///
/// This method is exception safe.
///
/// @param chg_type type of change to create CHG_ADD or CHG_REMOVE
/// @param lease Pointer to a lease for which NCR should be sent.
/// @param identifier Identifier to be used to generate DHCID for
/// the DNS update. For DHCPv4 it will be hardware address or client
/// identifier. For DHCPv6 it will be a DUID.
/// @param label Client identification information in the textual format.
/// This is used for logging purposes.
/// @param subnet subnet to which the lease belongs.
///
/// @tparam LeasePtrType Pointer to a lease.
/// @tparam IdentifierType HW Address, Client Identifier or DUID.
template<typename LeasePtrType, typename IdentifierType>
void queueNCRCommon(const NameChangeType& chg_type, const LeasePtrType& lease,
                    const IdentifierType& identifier, const std::string& label,
                    NetworkPtr subnet) {
    // Check if there is a need for update.
    if (lease->hostname_.empty() || (!lease->fqdn_fwd_ && !lease->fqdn_rev_)
        || !CfgMgr::instance().getD2ClientMgr().ddnsEnabled()) {
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
                  DHCPSRV_QUEUE_NCR_SKIP)
            .arg(label)
            .arg(lease->addr_.toText());

        return;
    }

     ConflictResolutionMode conflict_resolution_mode = CHECK_WITH_DHCID;
     util::Optional<double> ddns_ttl_percent;
     util::Optional<uint32_t> ddns_ttl;
     util::Optional<uint32_t> ddns_ttl_min;
     util::Optional<uint32_t> ddns_ttl_max;
     if (subnet) {
         auto mode = subnet->getDdnsConflictResolutionMode();
         if (!mode.empty()) {
             conflict_resolution_mode = StringToConflictResolutionMode(mode);
         }

         ddns_ttl_percent = subnet->getDdnsTtlPercent();
         ddns_ttl = subnet->getDdnsTtl();
         ddns_ttl_min = subnet->getDdnsTtlMin();
         ddns_ttl_max = subnet->getDdnsTtlMax();
     }

    try {
        // Create DHCID
        std::vector<uint8_t> hostname_wire;
        OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
        D2Dhcid dhcid = D2Dhcid(identifier, hostname_wire);

        // Calculate the TTL based on lease life time.
        uint32_t ttl = calculateDdnsTtl(lease->valid_lft_,
                                        ddns_ttl_percent, ddns_ttl,
                                        ddns_ttl_min, ddns_ttl_max);

        // Create name change request.
        NameChangeRequestPtr ncr
            (new NameChangeRequest(chg_type, lease->fqdn_fwd_, lease->fqdn_rev_,
                                   lease->hostname_, lease->addr_.toText(),
                                   dhcid, lease->cltt_ + ttl,
                                   ttl, conflict_resolution_mode));

        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA, DHCPSRV_QUEUE_NCR)
            .arg(label)
            .arg(chg_type == CHG_ADD ? "add" : "remove")
            .arg(ncr->toText());

        // Send name change request.
        CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);

    } catch (const std::exception& ex) {
        LOG_ERROR(dhcpsrv_logger, DHCPSRV_QUEUE_NCR_FAILED)
            .arg(label)
            .arg(chg_type == CHG_ADD ? "add" : "remove")
            .arg(lease->addr_.toText())
            .arg(ex.what());
    }
}

} // end of anonymous namespace

namespace isc {
namespace dhcp {

void queueNCR(const NameChangeType& chg_type, const Lease4Ptr& lease) {
    if (lease) {
        // Figure out from the lease's subnet if we should use conflict resolution.
        // If there's no subnet, something hinky is going on so we'll set it true.
        Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()
                            ->getCfgSubnets4()->getSubnet(lease->subnet_id_);

        // Client id takes precedence over HW address.
        if (lease->client_id_) {
            queueNCRCommon(chg_type, lease, lease->client_id_->getClientId(),
                           Pkt4::makeLabel(lease->hwaddr_, lease->client_id_), subnet);
        } else {
            // Client id is not specified for the lease. Use HW address
            // instead.
            queueNCRCommon(chg_type, lease, lease->hwaddr_,
                           Pkt4::makeLabel(lease->hwaddr_, lease->client_id_), subnet);
        }
    }
}

void queueNCR(const NameChangeType& chg_type, const Lease6Ptr& lease) {
    // DUID is required to generate NCR.
    if (lease && (lease->type_ != Lease::TYPE_PD) && lease->duid_) {
        // Figure out from the lease's subnet if we should use conflict resolution.
        // If there's no subnet, something hinky is going on so we'll set it true.
        Subnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()
                            ->getCfgSubnets6()->getSubnet(lease->subnet_id_);
        queueNCRCommon(chg_type, lease, *(lease->duid_),
                       Pkt6::makeLabel(lease->duid_, lease->hwaddr_), subnet);
    }
}

uint32_t calculateDdnsTtl(uint32_t lease_lft,
                          const util::Optional<double>& ddns_ttl_percent,
                          const util::Optional<uint32_t>& ddns_ttl,
                          const util::Optional<uint32_t>& ddns_ttl_min,
                          const util::Optional<uint32_t>& ddns_ttl_max) {
    //  If we have an explicit value use it.
    if (!ddns_ttl.unspecified() && ddns_ttl.get() > 0) {
        return (ddns_ttl.get());
    }

    // Use specified percentage (if one) or 1/3 as called for in RFC 4702.
    double ttl_percent = (ddns_ttl_percent.get() > 0.0 ?
                          ddns_ttl_percent.get() :  0.33333);

    // Calculate the ttl.
    uint32_t ttl = static_cast<uint32_t>(round(ttl_percent * lease_lft));

    // Adjust for minimum and maximum.
    // If we have a custom minimum enforce it, otherwise per RFC 4702 it
    // should not less than 600.
    uint32_t ttl_min = (ddns_ttl_min.get() > 0) ? ddns_ttl_min.get() : 600;
    if (ttl < ttl_min) {
        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
                      DHCPSRV_DDNS_TTL_TOO_SMALL)
                  .arg(util::str::dumpDouble(ttl_percent))
                  .arg(lease_lft)
                  .arg(ttl)
                  .arg(ttl_min);
        return (ttl_min);
    }

    // If we have a maximum enforce it.
    uint32_t ttl_max = ddns_ttl_max.get();
    if (ttl_max && ttl > ttl_max) {
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
                      DHCPSRV_DDNS_TTL_TOO_LARGE)
                     .arg(util::str::dumpDouble(ttl_percent))
                     .arg(lease_lft)
                     .arg(ttl)
                     .arg(ttl_max);
        return (ttl_max);
    }

    return (ttl);
}

}
}