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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
// Copyright (C) 2023-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Kea Hooks Basic
// Commercial End User License Agreement v2.0. See COPYING file in the premium/
// directory.

#include <config.h>
#include <database/audit_entry.h>
#include <dhcpsrv/cfgmgr.h>
#include <ping_check_log.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <ping_check_mgr.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <hooks/hooks.h>
#include <process/daemon.h>
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace ping_check {

/// @brief PingCheckMgr singleton
PingCheckMgrPtr mgr;

} // end of namespace ping_check
} // end of namespace isc

using namespace isc;
using namespace isc::log;
using namespace isc::data;
using namespace isc::db;
using namespace isc::dhcp;
using namespace isc::ping_check;
using namespace isc::hooks;
using namespace isc::process;
using namespace std;

// Functions accessed by the hooks framework use C linkage to avoid the name
// mangling that accompanies use of the C++ compiler as well as to avoid
// issues related to namespaces.
extern "C" {

/// @brief dhcp4_srv_configured implementation.
///
/// @param handle callout handle.
int dhcp4_srv_configured(CalloutHandle& handle) {
    try {
        isc::asiolink::IOServicePtr io_service;
        handle.getArgument("io_context", io_service);
        if (!io_service) {
            // Should not happen!
            handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
            const string error("Error: io_context is null");
            handle.setArgument("error", error);
            return (1);
        }

        SrvConfigPtr server_config;
        handle.getArgument("server_config", server_config);
        mgr->updateSubnetConfig(server_config);

        NetworkStatePtr network_state;
        handle.getArgument("network_state", network_state);

        // Schedule a start of the services. This ensures we begin after
        // the dust has settled and Kea MT mode has been firmly established.
        mgr->startService(io_service, network_state);
    } catch (const std::exception& ex) {
        LOG_ERROR(ping_check_logger, PING_CHECK_DHCP4_SRV_CONFIGURED_FAILED)
                  .arg(ex.what());

        handle.setStatus(isc::hooks::CalloutHandle::NEXT_STEP_DROP);
        ostringstream os;
        os << "Error: " << ex.what();
        string error(os.str());
        handle.setArgument("error", error);
        return (1);
    }

    return (0);
}

/// @brief cb4_updated callout implementation.
///
/// If it detects that any subnets were altered by the update it
/// replaces the subnet cache contents.  If any of the subnets
/// fail to parse, the error is logged and the function returns
/// a non-zero value.
///
/// @param handle CalloutHandle.
///
/// @return 0 upon success, 1 otherwise
int cb4_updated(CalloutHandle& handle) {<--- Parameter 'handle' can be declared as reference to const
    AuditEntryCollectionPtr audit_entries;
    handle.getArgument("audit_entries", audit_entries);

    auto const& object_type_idx = audit_entries->get<AuditEntryObjectTypeTag>();
    auto range = object_type_idx.equal_range("dhcp4_subnet");
    if (std::distance(range.first, range.second)) {
        try {
            // Server config has been committed, so use the current configuration.
            mgr->updateSubnetConfig(CfgMgr::instance().getCurrentCfg());
        } catch (const std::exception& ex) {
            LOG_ERROR(ping_check_logger, PING_CHECK_CB4_UPDATE_FAILED)
                      .arg(ex.what());
            return (1);
        }
    }

    return (0);
}

/// @brief lease4_offer callout implementation.
///
/// @param handle callout handle.
int lease4_offer(CalloutHandle& handle) {
    CalloutHandle::CalloutNextStep status = handle.getStatus();<--- Shadowed declaration
    if (status == CalloutHandle::NEXT_STEP_DROP ||
        status == CalloutHandle::NEXT_STEP_SKIP) {
        return (0);
    }

    Pkt4Ptr query4;
    Lease4Ptr lease4;
    ParkingLotHandlePtr parking_lot;<--- Shadowed declaration
    try {
        // Get all arguments available for the leases4_committed hook point.
        // If any of these arguments is not available this is a programmatic
        // error. An exception will be thrown which will be caught by the
        // caller and logged.
        handle.getArgument("query4", query4);

        Lease4CollectionPtr leases4;
        handle.getArgument("leases4", leases4);

        uint32_t offer_lifetime;
        handle.getArgument("offer_lifetime", offer_lifetime);

        Lease4Ptr old_lease;
        handle.getArgument("old_lease", old_lease);

        if (query4->getType() != DHCPDISCOVER) {
            isc_throw(InvalidOperation, "query4 is not a DHCPDISCOVER");
        }

        if (!leases4) {
            isc_throw(InvalidOperation, "leases4 is null");
        }

        if (!leases4->empty()) {
            lease4 = (*leases4)[0];
        }

        if (!lease4) {
            isc_throw(InvalidOperation, "leases4 is empty, no lease to check");
        }

        // Fetch the parking lot.  If it's empty the server is not employing
        // parking, which is fine.
        // Create a reference to the parked packet. This signals that we have a
        // stake in unparking it.
        ParkingLotHandlePtr parking_lot = handle.getParkingLotHandlePtr();<--- Shadow variable
        if (parking_lot) {
            parking_lot->reference(query4);
        }

        // Get configuration based on the lease's subnet.
        auto const& config = mgr->getScopedConfig(lease4);

        // Call shouldPing() to determine if we should ping check or not.
        // - status == PARK - ping check it
        // - status == CONTINUE - check not needed, release DHCPOFFER to client
        // - status == DROP - duplicate check, drop the duplicate DHCPOFFER
        auto status =  mgr->shouldPing(lease4, query4, old_lease, config);<--- Shadow variable
        handle.setStatus(status);
        if (status == CalloutHandle::NEXT_STEP_PARK) {
            mgr->startPing(lease4, query4, parking_lot, config);
        } else {
            // Dereference the parked packet.  This releases our stake in it.
            if (parking_lot) {
                parking_lot->dereference(query4);
            }
        }

    } catch (const std::exception& ex) {
        LOG_ERROR(ping_check_logger, PING_CHECK_LEASE4_OFFER_FAILED)
                  .arg(query4 ? query4->getLabel() : "<no query>")
                  .arg(lease4 ? lease4->addr_.toText() : "<no lease>")
                  .arg(ex.what());
        // Make sure we dereference.
        if (parking_lot) {
            parking_lot->dereference(query4);
        }

        return (1);
    }

    return (0);
}

/// @brief This function is called when the library is loaded.
///
/// @param handle library handle
/// @return 0 when initialization is successful, 1 otherwise
int load(LibraryHandle& handle) {
    try {
        // Make the hook library only loadable by kea-dhcp4.
        const string& proc_name = Daemon::getProcName();
        if (proc_name != "kea-dhcp4") {
            isc_throw(isc::Unexpected, "Bad process name: " << proc_name
                      << ", expected kea-dhcp4");
        }

        // Instantiate the manager singleton.
        mgr.reset(new PingCheckMgr());

        // Configure the manager using the hook library's parameters.
        ConstElementPtr json = handle.getParameters();
        mgr->configure(json);
    } catch (const exception& ex) {
        LOG_ERROR(ping_check_logger, PING_CHECK_LOAD_ERROR)
                  .arg(ex.what());
        return (1);
    }

    LOG_INFO(ping_check_logger, PING_CHECK_LOAD_OK);
    return (0);
}

/// @brief This function is called when the library is unloaded.
///
/// @return always 0.
int unload() {
    mgr.reset();
    LOG_INFO(ping_check_logger, PING_CHECK_UNLOAD);
    return (0);
}

/// @brief This function is called to retrieve the multi-threading compatibility.
///
/// @return 1 which means compatible with multi-threading.
int multi_threading_compatible() {
    return (1);
}

} // end extern "C"