Kea 2.7.1
lease_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2024 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
7
10#include <cc/data.h>
11#include <dhcp/hwaddr.h>
12#include <dhcpsrv/lease.h>
13#include <dhcpsrv/cfgmgr.h>
15#include <dhcpsrv/lease_mgr.h>
17#include <lease_parser.h>
18
19#include <config.h>
20
21using namespace std;
22using namespace isc::dhcp;
23using namespace isc::data;
24using namespace isc::asiolink;
25
26namespace isc {
27namespace lease_cmds {
28
31 const ConstElementPtr& lease_info,
32 bool& force_create) {
33 if (!lease_info) {
34 isc_throw(BadValue, "lease information missing");
35 }
36
37 // These are mandatory parameters.
38 IOAddress addr = getAddress(lease_info, "ip-address");
39 if (!addr.isV4()) {
40 isc_throw(BadValue, "Non-IPv4 address specified: " << addr);
41 }
42
43 // Not a most straightforward conversion, but it works.
44 string hwaddr_txt = getString(lease_info, "hw-address");
45 HWAddr hwaddr = HWAddr::fromText(hwaddr_txt);
46 HWAddrPtr hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr));
47
48 // Now sort out the subnet-id. If specified, it must have correct value.
49 // If not specified, Kea will try to sort it out.
50 SubnetID subnet_id = 0;
51 if (lease_info->contains("subnet-id")) {
52 subnet_id = getUint32(lease_info, "subnet-id");
53 }
54
55 uint32_t pool_id = 0;
56 if (lease_info->contains("pool-id")) {
57 pool_id = getUint32(lease_info, "pool-id");
58 }
59
60 // Check if the subnet-id specified is sane.
61 ConstSubnet4Ptr subnet;
62 if (subnet_id) {
63 // If subnet-id is specified, it has to match.
64 subnet = cfg->getCfgSubnets4()->getBySubnetId(subnet_id);
65 if (!subnet) {
66 isc_throw(LeaseCmdsConflict, "Invalid subnet-id: No IPv4 subnet with subnet-id="
67 << subnet_id << " currently configured.");
68 }
69
70 // Check if the address specified really belongs to the subnet.
71 if (!subnet->inRange(addr)) {
72 isc_throw(LeaseCmdsConflict, "The address " << addr.toText() << " does not belong "
73 "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id);
74 }
75
76 } else {
77 // Subnet-id was not specified. Let's try to figure it out on our own.
78 subnet = cfg->getCfgSubnets4()->selectSubnet(addr);
79 if (!subnet) {
80 isc_throw(LeaseCmdsConflict, "subnet-id not specified and failed to find a"
81 << " subnet for address " << addr);
82 }
83 subnet_id = subnet->getID();
84 }
85
86 // Client-id is optional.
87 ClientIdPtr client_id;
88 if (lease_info->contains("client-id")) {
89 string txt = getString(lease_info, "client-id");
90 client_id = ClientId::fromText(txt);
91 }
92
93 // These parameters are optional. If not specified, we'll derive them from
94 // the current subnet configuration, if possible.
95 uint32_t valid_lft = 0;
96 if (lease_info->contains("valid-lft")) {
97 valid_lft = getUint32(lease_info, "valid-lft");
98 } else {
99 valid_lft = subnet->getValid();
100 }
101
108 time_t cltt;
109 if (lease_info->contains("expire")) {
110 int64_t expire_time = getInteger(lease_info, "expire");
111 if (expire_time <= 0) {
112 isc_throw(BadValue , "expiration time must be positive for address "
113 << addr);
114
115 } else if (expire_time < valid_lft) {
116 isc_throw(BadValue, "expiration time must be greater than valid lifetime"
117 " for address " << addr);
118 }
119 cltt = static_cast<time_t>(expire_time - valid_lft);
120 } else {
121 cltt = time(NULL);
122 }
123
124 bool fqdn_fwd = false;
125 if (lease_info->contains("fqdn-fwd")) {
126 fqdn_fwd = getBoolean(lease_info, "fqdn-fwd");
127 }
128 bool fqdn_rev = false;
129 if (lease_info->contains("fqdn-rev")) {
130 fqdn_rev = getBoolean(lease_info, "fqdn-rev");
131 }
132 string hostname;
133 if (lease_info->contains("hostname")) {
134 hostname = getString(lease_info, "hostname");
135 }
136 if (hostname.empty() && (fqdn_fwd || fqdn_rev)) {
137 isc_throw(BadValue, "No hostname specified and either forward or reverse"
138 " fqdn was set to true.");
139 }
140
141 uint32_t state = 0;
142 if (lease_info->contains("state")) {
143 state = getUint8(lease_info, "state");
144 }
145
146 // Check if the state value is sane.
147 if (state > Lease::STATE_RELEASED) {
148 isc_throw(BadValue, "Invalid state value: " << state << ", supported "
149 "values are: 0 (default), 1 (declined), 2 (expired-reclaimed)"
150 " and 3 (released)");
151 }
152
153 // Handle user context.
154 ConstElementPtr ctx = lease_info->get("user-context");
155 if (ctx && (ctx->getType() != Element::map)) {
156 isc_throw(BadValue, "Invalid user context '" << ctx->str()
157 << "' is not a JSON map.");
158 }
159
160 // Handle comment.
161 ConstElementPtr comment = lease_info->get("comment");
162 if (comment) {
163 if (ctx && ctx->contains("comment")) {
164 isc_throw(BadValue, "Duplicated comment entry '" << comment->str()
165 << "' in user context '" << ctx->str() << "'");
166 }
167 ElementPtr copied;
168 if (ctx) {
169 copied = copy(ctx, 0);
170 } else {
171 copied = Element::createMap();
172 }
173 copied->set("comment", comment);
174 ctx = copied;
175 }
176
177 // Let's fabricate some data and we're ready to go.
178 Lease4Ptr l(new Lease4(addr, hwaddr_ptr, client_id, valid_lft,
179 cltt, subnet_id,
180 fqdn_fwd, fqdn_rev, hostname));
181 l->state_ = state;
182 l->setContext(ctx);
183 l->pool_id_ = pool_id;
184
185 // Sanitize extended info.
186 if (ctx) {
187 auto check = cfg->getConsistency()->getExtendedInfoSanityCheck();
190 }
191
192 // Retrieve the optional flag indicating if the lease must be created when it
193 // doesn't exist during the update.
194 force_create = false;
195 if (lease_info->contains("force-create")) {
196 force_create = getBoolean(lease_info, "force-create");
197 }
198
199 return (l);
200}
201
204 const ConstElementPtr& lease_info,
205 bool& force_create) {
206 if (!lease_info) {
207 isc_throw(BadValue, "lease information missing");
208 }
209
210 // These are mandatory parameters.
211 IOAddress addr = getAddress(lease_info, "ip-address");
212 if (addr.isV4()) {
213 isc_throw(BadValue, "Non-IPv6 address specified: " << addr);
214 }
215
216 // Not a most straightforward conversion, but it works.
217 string duid_txt = getString(lease_info, "duid");
218 DUID duid = DUID::fromText(duid_txt);
219 DuidPtr duid_ptr = DuidPtr(new DUID(duid));
220
222 uint8_t prefix_len = 128;
223 if (lease_info->contains("type")) {
224 string txt = getString(lease_info, "type");
225 if (txt == "IA_NA") {
226 type = Lease::TYPE_NA;
227 } else if (txt == "IA_TA") {
228 type = Lease::TYPE_TA;
229 } else if (txt == "IA_PD") {
230 type = Lease::TYPE_PD;
231
232 prefix_len = getUint8(lease_info, "prefix-len");
233 } else {
234 isc_throw(BadValue, "Incorrect lease type: " << txt << ", the only "
235 "supported values are: IA_NA and IA_PD");
236 }
237 }
238
239 // Now sort out the subnet-id. If specified, it must have correct value.
240 // If not specified, Kea will try to sort it out.
241 SubnetID subnet_id = 0;
242 if (lease_info->contains("subnet-id")) {
243 subnet_id = getUint32(lease_info, "subnet-id");
244 }
245
246 uint32_t pool_id = 0;
247 if (lease_info->contains("pool-id")) {
248 pool_id = getUint32(lease_info, "pool-id");
249 }
250
251 // Check if the subnet-id specified is sane.
252 ConstSubnet6Ptr subnet;
253 if (subnet_id) {
254 // If subnet-id is specified, it has to match.
255 subnet = cfg->getCfgSubnets6()->getBySubnetId(subnet_id);
256 if (!subnet) {
257 isc_throw(LeaseCmdsConflict, "Invalid subnet-id: No IPv6 subnet with subnet-id="
258 << subnet_id << " currently configured.");
259 }
260
261 // Check if the address specified really belongs to the subnet.
262 if ((type == Lease::TYPE_NA) && !subnet->inRange(addr)) {
263 isc_throw(LeaseCmdsConflict, "The address " << addr.toText() << " does not belong "
264 "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id);
265 }
266
267 } else {
268 if (type != Lease::TYPE_NA) {
269 isc_throw(BadValue, "Subnet-id is 0 or not specified. This is allowed for"
270 " address leases only, not prefix leases.");
271 }
272 // Subnet-id was not specified. Let's try to figure it out on our own.
273 subnet = cfg->getCfgSubnets6()->selectSubnet(addr);
274 if (!subnet) {
275 isc_throw(LeaseCmdsConflict, "subnet-id not specified and failed to find a "
276 "subnet for address " << addr);
277 }
278 subnet_id = subnet->getID();
279 }
280
281 uint32_t iaid = getUint32(lease_info, "iaid");
282
283 // Hw-address is optional in v6 leases.
284 HWAddrPtr hwaddr_ptr;
285 if (lease_info->contains("hw-address")) {
286 string hwaddr_txt = getString(lease_info, "hw-address");
287 HWAddr hwaddr = HWAddr::fromText(hwaddr_txt);
288 hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr));
289 }
290
291 // These parameters are optional. If not specified, we'll derive them
292 // from the current subnet configuration, if possible.
293 uint32_t valid_lft = 0;
294 if (lease_info->contains("valid-lft")) {
295 valid_lft = getUint32(lease_info, "valid-lft");
296 } else {
297 valid_lft = subnet->getValid();
298 }
299
300 // These parameters are optional. If not specified, we'll derive them
301 // from the current subnet configuration, if possible.
302 uint32_t pref_lft = 0;
303 if (lease_info->contains("preferred-lft")) {
304 pref_lft = getUint32(lease_info, "preferred-lft");
305 } else {
306 pref_lft = subnet->getValid();
307 }
308
315 time_t cltt;
316 if (lease_info->contains("expire")) {
317 int64_t expire_time = getInteger(lease_info, "expire");
318 if (expire_time <= 0) {
319 isc_throw(BadValue , "expiration time must be positive for address "
320 << addr);
321
322 } else if (expire_time < valid_lft) {
323 isc_throw(BadValue, "expiration time must be greater than valid lifetime"
324 " for address " << addr);
325 }
326
327 cltt = static_cast<time_t>(expire_time - valid_lft);
328 } else {
329 cltt = time(NULL);
330 }
331
332 bool fqdn_fwd = false;
333 if (lease_info->contains("fqdn-fwd")) {
334 fqdn_fwd = getBoolean(lease_info, "fqdn-fwd");
335 }
336 bool fqdn_rev = false;
337 if (lease_info->contains("fqdn-rev")) {
338 fqdn_rev = getBoolean(lease_info, "fqdn-rev");
339 }
340 string hostname;
341 if (lease_info->contains("hostname")) {
342 hostname = getString(lease_info, "hostname");
343 }
344 if (hostname.empty() && (fqdn_fwd || fqdn_rev)) {
345 isc_throw(BadValue, "No hostname specified and either forward or reverse"
346 " fqdn was set to true.");
347 }
348
349 uint32_t state = 0;
350 if (lease_info->contains("state")) {
351 state = getUint8(lease_info, "state");
352 }
353
354 // Check if the state value is sane.
355 if (state > Lease::STATE_RELEASED) {
356 isc_throw(BadValue, "Invalid state value: " << state << ", supported "
357 "values are: 0 (default), 1 (declined), 2 (expired-reclaimed)"
358 " and 3 (released)");
359 }
360
361 if ((state == Lease::STATE_DECLINED) && (type == Lease::TYPE_PD)) {
363 "Invalid declined state for PD prefix.");
364 }
365
366 // Handle user context.
367 ConstElementPtr ctx = lease_info->get("user-context");
368 if (ctx && (ctx->getType() != Element::map)) {
369 isc_throw(BadValue, "Invalid user context '" << ctx->str()
370 << "' is not a JSON map.");
371 }
372
373 // Handle comment.
374 ConstElementPtr comment = lease_info->get("comment");
375 if (comment) {
376 if (ctx && ctx->contains("comment")) {
377 isc_throw(BadValue, "Duplicated comment entry '" << comment->str()
378 << "' in user context '" << ctx->str() << "'");
379 }
380 ElementPtr copied;
381 if (ctx) {
382 copied = copy(ctx, 0);
383 } else {
384 copied = Element::createMap();
385 }
386 copied->set("comment", comment);
387 ctx = copied;
388 }
389
390 // Check if the prefix length is sane
391 if (prefix_len == 0 || prefix_len > 128) {
392 isc_throw(BadValue, "Invalid prefix length: "
393 << static_cast<unsigned>(prefix_len));
394 }
395
396 if (prefix_len != 128) {
397 IOAddress first_address = firstAddrInPrefix(addr, prefix_len);
398 if (first_address != addr) {
399 isc_throw(BadValue, "Prefix address: " << addr
400 << " exceeds prefix/prefix-len pair: " << first_address
401 << "/" << static_cast<uint32_t>(prefix_len));
402 }
403 }
404
405 // Let's fabricate some data and we're ready to go.
406 Lease6Ptr l(new Lease6(type, addr, duid_ptr, iaid, pref_lft, valid_lft,
407 subnet_id, fqdn_fwd, fqdn_rev, hostname,
408 hwaddr_ptr, prefix_len));
409 l->cltt_ = cltt;
410 l->state_ = state;
411 l->setContext(ctx);
412 l->pool_id_ = pool_id;
413
414 // Sanitize extended info.
415 if (ctx) {
416 auto check = cfg->getConsistency()->getExtendedInfoSanityCheck();
418 }
419
420 // Retrieve the optional flag indicating if the lease must be created when it
421 // doesn't exist during the update.
422 force_create = false;
423 if (lease_info->contains("force-create")) {
424 force_create = getBoolean(lease_info, "force-create");
425 }
426
427 return (l);
428}
429
430} // end of namespace lease_cmds
431} // end of namespace isc
Exception thrown when a command failed due to a conflict.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a function is called in a prohibited way.
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
uint8_t getUint8(ConstElementPtr scope, const std::string &name)
Get an uint8_t value.
static isc::asiolink::IOAddress getAddress(const ConstElementPtr &scope, const std::string &name)
Returns a IOAddress parameter from a scope.
static std::string getString(isc::data::ConstElementPtr scope, const std::string &name)
Returns a string parameter from a scope.
uint32_t getUint32(isc::data::ConstElementPtr scope, const std::string &name)
Returns a value converted to uint32_t.
static bool getBoolean(isc::data::ConstElementPtr scope, const std::string &name)
Returns a boolean parameter from a scope.
static int64_t getInteger(isc::data::ConstElementPtr scope, const std::string &name)
Returns an integer parameter from a scope.
static ClientIdPtr fromText(const std::string &text)
Create client identifier from the textual format.
Definition duid.cc:73
Holds DUID (DHCPv6 Unique Identifier)
Definition duid.h:142
static DUID fromText(const std::string &text)
Create DUID from the textual format.
Definition duid.cc:50
static bool upgradeLease6ExtendedInfo(const Lease6Ptr &lease, CfgConsistency::ExtendedInfoSanity check=CfgConsistency::EXTENDED_INFO_CHECK_FIX)
Upgrade a V6 lease user context to the new extended info entry.
Definition lease_mgr.cc:756
static void extractLease4ExtendedInfo(const Lease4Ptr &lease, bool ignore_errors=true)
Extract relay and remote identifiers from the extended info.
static bool upgradeLease4ExtendedInfo(const Lease4Ptr &lease, CfgConsistency::ExtendedInfoSanity check=CfgConsistency::EXTENDED_INFO_CHECK_FIX)
The following queries are used to fulfill Bulk Lease Query queries.
Definition lease_mgr.cc:535
virtual isc::dhcp::Lease4Ptr parse(isc::dhcp::ConstSrvConfigPtr &cfg, const isc::data::ConstElementPtr &lease_info, bool &force_create)
Parses Element tree and tries to convert to Lease4.
virtual isc::dhcp::Lease6Ptr parse(isc::dhcp::ConstSrvConfigPtr &cfg, const isc::data::ConstElementPtr &lease_info, bool &force_create)
Parses Element tree and tries to convert to Lease4.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
An abstract API for lease database.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition data.cc:1420
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
boost::shared_ptr< const SrvConfig > ConstSrvConfigPtr
Const pointer to the SrvConfig.
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:620
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:455
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition lease.h:508
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition duid.h:216
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition lease.h:295
Defines the logger used by the top-level component of kea-lfc.
Hardware type that represents information from DHCPv4 packet.
Definition hwaddr.h:20
static HWAddr fromText(const std::string &text, const uint16_t htype=HTYPE_ETHER)
Creates instance of the hardware address from textual format.
Definition hwaddr.cc:69
Structure that holds a lease for IPv4 address.
Definition lease.h:303
Structure that holds a lease for IPv6 address and/or prefix.
Definition lease.h:516
static const uint32_t STATE_DECLINED
Declined lease.
Definition lease.h:72
static const uint32_t STATE_RELEASED
Released lease held in the database for lease affinity.
Definition lease.h:78
Type
Type of lease or pool.
Definition lease.h:46
@ TYPE_TA
the lease contains temporary IPv6 address
Definition lease.h:48
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition lease.h:49
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:47