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