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