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