Coverage Report

Created: 2026-08-14 08:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea/src/lib/dhcpsrv/sanity_checker.cc
Line
Count
Source
1
// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
2
//
3
// This Source Code Form is subject to the terms of the Mozilla Public
4
// License, v. 2.0. If a copy of the MPL was not distributed with this
5
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
#include <config.h>
7
8
#include <dhcpsrv/sanity_checker.h>
9
#include <dhcpsrv/cfg_consistency.h>
10
#include <dhcpsrv/cfgmgr.h>
11
#include <dhcpsrv/subnet_id.h>
12
#include <dhcpsrv/dhcpsrv_log.h>
13
#include <sstream>
14
15
namespace isc {
16
namespace dhcp {
17
18
15.3k
bool SanityChecker::leaseCheckingEnabled(bool current) {
19
15.3k
    SrvConfigPtr cfg;
20
15.3k
    if (current) {
21
0
        cfg = CfgMgr::instance().getCurrentCfg();
22
15.3k
    } else {
23
15.3k
        cfg = CfgMgr::instance().getStagingCfg();
24
15.3k
    }
25
26
15.3k
    if (cfg) {
27
15.3k
        CfgConsistencyPtr sanity = cfg->getConsistency();
28
15.3k
        return (sanity && (sanity->getLeaseSanityCheck() != CfgConsistency::LEASE_CHECK_NONE));
29
15.3k
    }
30
31
0
    return (false);
32
15.3k
}
33
34
32.6k
void SanityChecker::checkLease(Lease4Ptr& lease, bool current) {
35
32.6k
    SrvConfigPtr cfg;
36
32.6k
    if (current) {
37
0
        cfg = CfgMgr::instance().getCurrentCfg();
38
32.6k
    } else {
39
32.6k
        cfg = CfgMgr::instance().getStagingCfg();
40
32.6k
    }
41
42
32.6k
    CfgConsistencyPtr sanity = cfg->getConsistency();
43
32.6k
    if (sanity->getLeaseSanityCheck() == CfgConsistency::LEASE_CHECK_NONE) {
44
        // No sense going farther.
45
0
        return;
46
0
    }
47
48
32.6k
    CfgSubnets4Ptr subnets = cfg->getCfgSubnets4();
49
32.6k
    checkLeaseInternal(lease, sanity, subnets);
50
32.6k
}
51
52
24.1k
void SanityChecker::checkLease(Lease6Ptr& lease, bool current) {
53
    // We only check IA_NAs currently.
54
24.1k
    if (lease->type_ != Lease::TYPE_NA) {
55
18.0k
        return;
56
18.0k
    }
57
58
6.03k
    SrvConfigPtr cfg;
59
6.03k
    if (current) {
60
0
        cfg = CfgMgr::instance().getCurrentCfg();
61
6.03k
    } else {
62
6.03k
        cfg = CfgMgr::instance().getStagingCfg();
63
6.03k
    }
64
6.03k
    CfgConsistencyPtr sanity = cfg->getConsistency();
65
6.03k
    if (sanity->getLeaseSanityCheck() == CfgConsistency::LEASE_CHECK_NONE) {
66
        // No sense going farther.
67
0
        return;
68
0
    }
69
70
6.03k
    CfgSubnets6Ptr subnets = cfg->getCfgSubnets6();
71
6.03k
    checkLeaseInternal(lease, sanity, subnets);
72
6.03k
}
73
74
template<typename LeasePtrType, typename SubnetsType>
75
void SanityChecker::checkLeaseInternal(LeasePtrType& lease, const CfgConsistencyPtr& checks,
76
38.6k
                               const SubnetsType& subnets) {
77
78
38.6k
    auto subnet = subnets->getBySubnetId(lease->subnet_id_);
79
38.6k
    if (subnet && subnet->inRange(lease->addr_)) {
80
81
        // If the subnet is defined and the address is in range, we're good.
82
83
8.15k
        return;
84
8.15k
    }
85
86
    // Ok, if we got here, that means that either we did not find a subnet
87
    // of found it, but it wasn't the right subnet.
88
30.4k
    SubnetID id = findSubnetId(lease, subnets);
89
90
    // Prepare a message in the case the check fails.
91
30.4k
    std::ostringstream msg;
92
30.4k
    if (id != 0) {
93
24.4k
        msg << "the lease should have subnet-id " << id;
94
24.4k
    } else {
95
6.03k
        msg << "the lease IP address did not belong to a configured subnet";
96
6.03k
    }
97
98
30.4k
    switch (checks->getLeaseSanityCheck()) {
99
30.4k
    case CfgConsistency::LEASE_CHECK_WARN:
100
30.4k
        if (lease->subnet_id_ != id) {
101
            // Print a warning, but return the lease as is.
102
30.4k
            LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL)
103
30.4k
                .arg(lease->addr_.toText())
104
30.4k
                .arg(lease->subnet_id_)
105
30.4k
                .arg(msg.str());
106
30.4k
        }
107
30.4k
        break;
108
109
0
    case CfgConsistency::LEASE_CHECK_FIX:
110
0
        if (lease->subnet_id_ != id) {
111
112
            // If there is a better subnet, use it.
113
0
            if (id != 0) {
114
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FIXED)
115
0
                    .arg(lease->addr_.toText())
116
0
                    .arg(lease->subnet_id_)
117
0
                    .arg(id);
118
0
                lease->subnet_id_ = id;
119
0
            } else {
120
                // If not, return the lease as is.
121
0
                LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL)
122
0
                    .arg(lease->addr_.toText())
123
0
                    .arg(lease->subnet_id_)
124
0
                    .arg(msg.str());
125
0
            }
126
0
        }
127
0
        break;
128
129
0
    case CfgConsistency::LEASE_CHECK_FIX_DEL:
130
0
        if (lease->subnet_id_ != id) {
131
132
            // If there is a better subnet, use it.
133
0
            if (id != 0) {
134
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FIXED)
135
0
                    .arg(lease->addr_.toText())
136
0
                    .arg(lease->subnet_id_)
137
0
                    .arg(id);
138
0
                lease->subnet_id_ = id;
139
0
                break;
140
0
            } else {
141
                // If not, delete the lease.
142
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL_DISCARD)
143
0
                    .arg(lease->addr_.toText())
144
0
                    .arg(lease->subnet_id_)
145
0
                    .arg(msg.str());
146
0
                lease.reset();
147
0
            }
148
149
0
        }
150
0
        break;
151
152
0
    case CfgConsistency::LEASE_CHECK_DEL:
153
0
        if (lease->subnet_id_ != id) {
154
0
            LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL_DISCARD)
155
0
                .arg(lease->addr_.toText())
156
0
                .arg(lease->subnet_id_)
157
0
                .arg(msg.str());
158
0
            lease.reset();
159
0
        }
160
0
        break;
161
162
0
    default:
163
        // Shouldn't get here but some compilers and analyzers
164
        // complain.  We'll we treat it as NONE and return the
165
        // lease as-is.
166
0
        break;
167
168
30.4k
    }
169
170
    // Additional checks may be implemented in the future here.
171
172
    /// @todo: add a check if the address is within specified dynamic pool
173
    /// if not, check if the address is reserved.
174
30.4k
}
void isc::dhcp::SanityChecker::checkLeaseInternal<boost::shared_ptr<isc::dhcp::Lease4>, boost::shared_ptr<isc::dhcp::CfgSubnets4> >(boost::shared_ptr<isc::dhcp::Lease4>&, boost::shared_ptr<isc::dhcp::CfgConsistency> const&, boost::shared_ptr<isc::dhcp::CfgSubnets4> const&)
Line
Count
Source
76
32.6k
                               const SubnetsType& subnets) {
77
78
32.6k
    auto subnet = subnets->getBySubnetId(lease->subnet_id_);
79
32.6k
    if (subnet && subnet->inRange(lease->addr_)) {
80
81
        // If the subnet is defined and the address is in range, we're good.
82
83
8.15k
        return;
84
8.15k
    }
85
86
    // Ok, if we got here, that means that either we did not find a subnet
87
    // of found it, but it wasn't the right subnet.
88
24.4k
    SubnetID id = findSubnetId(lease, subnets);
89
90
    // Prepare a message in the case the check fails.
91
24.4k
    std::ostringstream msg;
92
24.4k
    if (id != 0) {
93
24.4k
        msg << "the lease should have subnet-id " << id;
94
24.4k
    } else {
95
0
        msg << "the lease IP address did not belong to a configured subnet";
96
0
    }
97
98
24.4k
    switch (checks->getLeaseSanityCheck()) {
99
24.4k
    case CfgConsistency::LEASE_CHECK_WARN:
100
24.4k
        if (lease->subnet_id_ != id) {
101
            // Print a warning, but return the lease as is.
102
24.4k
            LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL)
103
24.4k
                .arg(lease->addr_.toText())
104
24.4k
                .arg(lease->subnet_id_)
105
24.4k
                .arg(msg.str());
106
24.4k
        }
107
24.4k
        break;
108
109
0
    case CfgConsistency::LEASE_CHECK_FIX:
110
0
        if (lease->subnet_id_ != id) {
111
112
            // If there is a better subnet, use it.
113
0
            if (id != 0) {
114
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FIXED)
115
0
                    .arg(lease->addr_.toText())
116
0
                    .arg(lease->subnet_id_)
117
0
                    .arg(id);
118
0
                lease->subnet_id_ = id;
119
0
            } else {
120
                // If not, return the lease as is.
121
0
                LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL)
122
0
                    .arg(lease->addr_.toText())
123
0
                    .arg(lease->subnet_id_)
124
0
                    .arg(msg.str());
125
0
            }
126
0
        }
127
0
        break;
128
129
0
    case CfgConsistency::LEASE_CHECK_FIX_DEL:
130
0
        if (lease->subnet_id_ != id) {
131
132
            // If there is a better subnet, use it.
133
0
            if (id != 0) {
134
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FIXED)
135
0
                    .arg(lease->addr_.toText())
136
0
                    .arg(lease->subnet_id_)
137
0
                    .arg(id);
138
0
                lease->subnet_id_ = id;
139
0
                break;
140
0
            } else {
141
                // If not, delete the lease.
142
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL_DISCARD)
143
0
                    .arg(lease->addr_.toText())
144
0
                    .arg(lease->subnet_id_)
145
0
                    .arg(msg.str());
146
0
                lease.reset();
147
0
            }
148
149
0
        }
150
0
        break;
151
152
0
    case CfgConsistency::LEASE_CHECK_DEL:
153
0
        if (lease->subnet_id_ != id) {
154
0
            LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL_DISCARD)
155
0
                .arg(lease->addr_.toText())
156
0
                .arg(lease->subnet_id_)
157
0
                .arg(msg.str());
158
0
            lease.reset();
159
0
        }
160
0
        break;
161
162
0
    default:
163
        // Shouldn't get here but some compilers and analyzers
164
        // complain.  We'll we treat it as NONE and return the
165
        // lease as-is.
166
0
        break;
167
168
24.4k
    }
169
170
    // Additional checks may be implemented in the future here.
171
172
    /// @todo: add a check if the address is within specified dynamic pool
173
    /// if not, check if the address is reserved.
174
24.4k
}
void isc::dhcp::SanityChecker::checkLeaseInternal<boost::shared_ptr<isc::dhcp::Lease6>, boost::shared_ptr<isc::dhcp::CfgSubnets6> >(boost::shared_ptr<isc::dhcp::Lease6>&, boost::shared_ptr<isc::dhcp::CfgConsistency> const&, boost::shared_ptr<isc::dhcp::CfgSubnets6> const&)
Line
Count
Source
76
6.03k
                               const SubnetsType& subnets) {
77
78
6.03k
    auto subnet = subnets->getBySubnetId(lease->subnet_id_);
79
6.03k
    if (subnet && subnet->inRange(lease->addr_)) {
80
81
        // If the subnet is defined and the address is in range, we're good.
82
83
0
        return;
84
0
    }
85
86
    // Ok, if we got here, that means that either we did not find a subnet
87
    // of found it, but it wasn't the right subnet.
88
6.03k
    SubnetID id = findSubnetId(lease, subnets);
89
90
    // Prepare a message in the case the check fails.
91
6.03k
    std::ostringstream msg;
92
6.03k
    if (id != 0) {
93
0
        msg << "the lease should have subnet-id " << id;
94
6.03k
    } else {
95
6.03k
        msg << "the lease IP address did not belong to a configured subnet";
96
6.03k
    }
97
98
6.03k
    switch (checks->getLeaseSanityCheck()) {
99
6.03k
    case CfgConsistency::LEASE_CHECK_WARN:
100
6.03k
        if (lease->subnet_id_ != id) {
101
            // Print a warning, but return the lease as is.
102
6.03k
            LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL)
103
6.03k
                .arg(lease->addr_.toText())
104
6.03k
                .arg(lease->subnet_id_)
105
6.03k
                .arg(msg.str());
106
6.03k
        }
107
6.03k
        break;
108
109
0
    case CfgConsistency::LEASE_CHECK_FIX:
110
0
        if (lease->subnet_id_ != id) {
111
112
            // If there is a better subnet, use it.
113
0
            if (id != 0) {
114
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FIXED)
115
0
                    .arg(lease->addr_.toText())
116
0
                    .arg(lease->subnet_id_)
117
0
                    .arg(id);
118
0
                lease->subnet_id_ = id;
119
0
            } else {
120
                // If not, return the lease as is.
121
0
                LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL)
122
0
                    .arg(lease->addr_.toText())
123
0
                    .arg(lease->subnet_id_)
124
0
                    .arg(msg.str());
125
0
            }
126
0
        }
127
0
        break;
128
129
0
    case CfgConsistency::LEASE_CHECK_FIX_DEL:
130
0
        if (lease->subnet_id_ != id) {
131
132
            // If there is a better subnet, use it.
133
0
            if (id != 0) {
134
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FIXED)
135
0
                    .arg(lease->addr_.toText())
136
0
                    .arg(lease->subnet_id_)
137
0
                    .arg(id);
138
0
                lease->subnet_id_ = id;
139
0
                break;
140
0
            } else {
141
                // If not, delete the lease.
142
0
                LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL_DISCARD)
143
0
                    .arg(lease->addr_.toText())
144
0
                    .arg(lease->subnet_id_)
145
0
                    .arg(msg.str());
146
0
                lease.reset();
147
0
            }
148
149
0
        }
150
0
        break;
151
152
0
    case CfgConsistency::LEASE_CHECK_DEL:
153
0
        if (lease->subnet_id_ != id) {
154
0
            LOG_INFO(dhcpsrv_logger, DHCPSRV_LEASE_SANITY_FAIL_DISCARD)
155
0
                .arg(lease->addr_.toText())
156
0
                .arg(lease->subnet_id_)
157
0
                .arg(msg.str());
158
0
            lease.reset();
159
0
        }
160
0
        break;
161
162
0
    default:
163
        // Shouldn't get here but some compilers and analyzers
164
        // complain.  We'll we treat it as NONE and return the
165
        // lease as-is.
166
0
        break;
167
168
6.03k
    }
169
170
    // Additional checks may be implemented in the future here.
171
172
    /// @todo: add a check if the address is within specified dynamic pool
173
    /// if not, check if the address is reserved.
174
6.03k
}
175
176
template<typename LeaseType, typename SubnetsType>
177
30.4k
SubnetID SanityChecker::findSubnetId(const LeaseType& lease, const SubnetsType& subnets) {
178
30.4k
    auto subnet = subnets->selectSubnet(lease->addr_);
179
30.4k
    if (!subnet) {
180
6.03k
        return (0);
181
6.03k
    }
182
183
24.4k
    return (subnet->getID());
184
30.4k
}
unsigned int isc::dhcp::SanityChecker::findSubnetId<boost::shared_ptr<isc::dhcp::Lease4>, boost::shared_ptr<isc::dhcp::CfgSubnets4> >(boost::shared_ptr<isc::dhcp::Lease4> const&, boost::shared_ptr<isc::dhcp::CfgSubnets4> const&)
Line
Count
Source
177
24.4k
SubnetID SanityChecker::findSubnetId(const LeaseType& lease, const SubnetsType& subnets) {
178
24.4k
    auto subnet = subnets->selectSubnet(lease->addr_);
179
24.4k
    if (!subnet) {
180
0
        return (0);
181
0
    }
182
183
24.4k
    return (subnet->getID());
184
24.4k
}
unsigned int isc::dhcp::SanityChecker::findSubnetId<boost::shared_ptr<isc::dhcp::Lease6>, boost::shared_ptr<isc::dhcp::CfgSubnets6> >(boost::shared_ptr<isc::dhcp::Lease6> const&, boost::shared_ptr<isc::dhcp::CfgSubnets6> const&)
Line
Count
Source
177
6.03k
SubnetID SanityChecker::findSubnetId(const LeaseType& lease, const SubnetsType& subnets) {
178
6.03k
    auto subnet = subnets->selectSubnet(lease->addr_);
179
6.03k
    if (!subnet) {
180
6.03k
        return (0);
181
6.03k
    }
182
183
0
    return (subnet->getID());
184
6.03k
}
185
186
}
187
}