Kea  2.3.7
lease_mgr.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-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 <config.h>
8 
9 #include <dhcp/libdhcp++.h>
10 #include <dhcp/option_custom.h>
11 #include <dhcpsrv/cfgmgr.h>
12 #include <dhcpsrv/dhcpsrv_log.h>
13 #include <dhcpsrv/lease_mgr.h>
14 #include <exceptions/exceptions.h>
15 #include <stats/stats_mgr.h>
16 #include <util/encode/hex.h>
17 
18 #include <boost/foreach.hpp>
19 #include <boost/algorithm/string.hpp>
20 
21 #include <algorithm>
22 #include <iostream>
23 #include <iterator>
24 #include <map>
25 #include <sstream>
26 #include <string>
27 
28 #include <time.h>
29 
30 using namespace isc::asiolink;
31 using namespace isc::data;
32 using namespace isc::db;
33 using namespace isc::dhcp;
34 using namespace isc::util;
35 using namespace std;
36 
37 namespace isc {
38 namespace dhcp {
39 
40 IOServicePtr LeaseMgr::io_service_ = IOServicePtr();
41 
42 LeasePageSize::LeasePageSize(const size_t page_size)
43  : page_size_(page_size) {
44 
45  if (page_size_ == 0) {
46  isc_throw(OutOfRange, "page size of retrieved leases must not be 0");
47  }
48 
49  if (page_size_ > std::numeric_limits<uint32_t>::max()) {
50  isc_throw(OutOfRange, "page size of retrieved leases must not be greater than "
51  << std::numeric_limits<uint32_t>::max());
52  }
53 }
54 
57  uint32_t iaid, SubnetID subnet_id) const {
58  Lease6Collection col = getLeases6(type, duid, iaid, subnet_id);
59 
60  if (col.size() > 1) {
61  isc_throw(MultipleRecords, "More than one lease found for type "
62  << static_cast<int>(type) << ", duid "
63  << duid.toText() << ", iaid " << iaid
64  << " and subnet-id " << subnet_id);
65  }
66  if (col.empty()) {
67  return (Lease6Ptr());
68  }
69  return (*col.begin());
70 }
71 
72 void
74  using namespace stats;
75 
76  StatsMgr& stats_mgr = StatsMgr::instance();
77 
79  if (!query) {
81  return;
82  }
83 
84  // Zero out the global stats.
85  // Cumulative counters ("reclaimed-declined-addresses", "reclaimed-leases",
86  // "cumulative-assigned-addresses") never get zeroed.
87  int64_t zero = 0;
88  stats_mgr.setValue("declined-addresses", zero);
89 
90  // Create if it does not exit reclaimed declined leases global stats.
91  if (!stats_mgr.getObservation("reclaimed-declined-addresses")) {
92  stats_mgr.setValue("reclaimed-declined-addresses", zero);
93  }
94 
95  // Create if it does not exit reclaimed leases global stats.
96  if (!stats_mgr.getObservation("reclaimed-leases")) {
97  stats_mgr.setValue("reclaimed-leases", zero);
98  }
99 
100  // Create if it does not exit cumulative global stats.
101  if (!stats_mgr.getObservation("cumulative-assigned-addresses")) {
102  stats_mgr.setValue("cumulative-assigned-addresses", zero);
103  }
104 
105  // Clear subnet level stats. This ensures we don't end up with corner
106  // cases that leave stale values in place.
107  const Subnet4Collection* subnets =
108  CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
109 
110  for (Subnet4Collection::const_iterator subnet = subnets->begin();
111  subnet != subnets->end(); ++subnet) {
112  SubnetID subnet_id = (*subnet)->getID();
113  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
114  "assigned-addresses"),
115  zero);
116 
117  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
118  "declined-addresses"),
119  zero);
120 
121  if (!stats_mgr.getObservation(
122  StatsMgr::generateName("subnet", subnet_id,
123  "reclaimed-declined-addresses"))) {
124  stats_mgr.setValue(
125  StatsMgr::generateName("subnet", subnet_id,
126  "reclaimed-declined-addresses"),
127  zero);
128  }
129 
130  if (!stats_mgr.getObservation(
131  StatsMgr::generateName("subnet", subnet_id,
132  "reclaimed-leases"))) {
133  stats_mgr.setValue(
134  StatsMgr::generateName("subnet", subnet_id,
135  "reclaimed-leases"),
136  zero);
137  }
138  }
139 
140  // Get counts per state per subnet. Iterate over the result set
141  // updating the subnet and global values.
142  LeaseStatsRow row;
143  while (query->getNextRow(row)) {
144  if (row.lease_state_ == Lease::STATE_DEFAULT) {
145  // Add to subnet level value.
146  stats_mgr.addValue(StatsMgr::generateName("subnet", row.subnet_id_,
147  "assigned-addresses"),
148  row.state_count_);
149  } else if (row.lease_state_ == Lease::STATE_DECLINED) {
150  // Set subnet level value.
151  stats_mgr.setValue(StatsMgr::generateName("subnet", row.subnet_id_,
152  "declined-addresses"),
153  row.state_count_);
154 
155  // Add to the global value.
156  stats_mgr.addValue("declined-addresses", row.state_count_);
157 
158  // Add to subnet level value.
159  // Declined leases also count as assigned.
160  stats_mgr.addValue(StatsMgr::generateName("subnet", row.subnet_id_,
161  "assigned-addresses"),
162  row.state_count_);
163  }
164  }
165 }
166 
168  : first_subnet_id_(0), last_subnet_id_(0), select_mode_(ALL_SUBNETS) {
169 }
170 
172  : first_subnet_id_(subnet_id), last_subnet_id_(0),
173  select_mode_(SINGLE_SUBNET) {
174 
175  if (first_subnet_id_ == 0) {
176  isc_throw(BadValue, "LeaseStatsQuery: subnet_id_ must be > 0");
177  }
178 }
179 
181  const SubnetID& last_subnet_id)
182  : first_subnet_id_(first_subnet_id), last_subnet_id_(last_subnet_id),
183  select_mode_(SUBNET_RANGE) {
184 
185  if (first_subnet_id_ == 0) {
186  isc_throw(BadValue, "LeaseStatsQuery: first_subnet_id_ must be > 0");
187  }
188 
189  if (last_subnet_id_ == 0) {
190  isc_throw(BadValue, "LeaseStatsQuery: last_subnet_id_ must be > 0");
191  }
192 
195  "LeaseStatsQuery: last_subnet_id_must be > first_subnet_id_");
196  }
197 }
198 
201  return(LeaseStatsQueryPtr());
202 }
203 
206  return(LeaseStatsQueryPtr());
207 }
208 
211  const SubnetID& /* last_subnet_id */) {
212  return(LeaseStatsQueryPtr());
213 }
214 
215 bool
217  return (false);
218 }
219 
220 void
222  using namespace stats;
223 
224  StatsMgr& stats_mgr = StatsMgr::instance();
225 
227  if (!query) {
229  return;
230  }
231 
232  // Zero out the global stats.
233  // Cumulative counters ("reclaimed-declined-addresses", "reclaimed-leases",
234  // "cumulative-assigned-nas", "cumulative-assigned-pds") never get zeroed.
235  int64_t zero = 0;
236  stats_mgr.setValue("declined-addresses", zero);
237 
238  if (!stats_mgr.getObservation("reclaimed-declined-addresses")) {
239  stats_mgr.setValue("reclaimed-declined-addresses", zero);
240  }
241 
242  if (!stats_mgr.getObservation("reclaimed-leases")) {
243  stats_mgr.setValue("reclaimed-leases", zero);
244  }
245 
246  // Create if it does not exit cumulative nas global stats.
247  if (!stats_mgr.getObservation("cumulative-assigned-nas")) {
248  stats_mgr.setValue("cumulative-assigned-nas", zero);
249  }
250 
251  // Create if it does not exit cumulative pds global stats.
252  if (!stats_mgr.getObservation("cumulative-assigned-pds")) {
253  stats_mgr.setValue("cumulative-assigned-pds", zero);
254  }
255 
256  // Clear subnet level stats. This ensures we don't end up with corner
257  // cases that leave stale values in place.
258  const Subnet6Collection* subnets =
259  CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
260 
261  for (Subnet6Collection::const_iterator subnet = subnets->begin();
262  subnet != subnets->end(); ++subnet) {
263  SubnetID subnet_id = (*subnet)->getID();
264  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
265  "assigned-nas"),
266  zero);
267 
268  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
269  "declined-addresses"),
270  zero);
271 
272  if (!stats_mgr.getObservation(
273  StatsMgr::generateName("subnet", subnet_id,
274  "reclaimed-declined-addresses"))) {
275  stats_mgr.setValue(
276  StatsMgr::generateName("subnet", subnet_id,
277  "reclaimed-declined-addresses"),
278  zero);
279  }
280 
281  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
282  "assigned-pds"),
283  zero);
284 
285  if (!stats_mgr.getObservation(
286  StatsMgr::generateName("subnet", subnet_id,
287  "reclaimed-leases"))) {
288  stats_mgr.setValue(
289  StatsMgr::generateName("subnet", subnet_id,
290  "reclaimed-leases"),
291  zero);
292  }
293  }
294 
295  // Get counts per state per subnet. Iterate over the result set
296  // updating the subnet and global values.
297  LeaseStatsRow row;
298  while (query->getNextRow(row)) {
299  switch(row.lease_type_) {
300  case Lease::TYPE_NA:
301  if (row.lease_state_ == Lease::STATE_DEFAULT) {
302  // Add to subnet level value.
303  stats_mgr.addValue(StatsMgr::
304  generateName("subnet", row.subnet_id_,
305  "assigned-nas"),
306  row.state_count_);
307  } else if (row.lease_state_ == Lease::STATE_DECLINED) {
308  // Set subnet level value.
309  stats_mgr.setValue(StatsMgr::
310  generateName("subnet", row.subnet_id_,
311  "declined-addresses"),
312  row.state_count_);
313 
314  // Add to the global value.
315  stats_mgr.addValue("declined-addresses", row.state_count_);
316 
317  // Add to subnet level value.
318  // Declined leases also count as assigned.
319  stats_mgr.addValue(StatsMgr::
320  generateName("subnet", row.subnet_id_,
321  "assigned-nas"),
322  row.state_count_);
323  }
324  break;
325 
326  case Lease::TYPE_PD:
327  if (row.lease_state_ == Lease::STATE_DEFAULT) {
328  // Set subnet level value.
329  stats_mgr.setValue(StatsMgr::
330  generateName("subnet", row.subnet_id_,
331  "assigned-pds"),
332  row.state_count_);
333  }
334  break;
335 
336  default:
337  // We dont' support TYPE_TAs yet
338  break;
339  }
340  }
341 }
342 
345  return(LeaseStatsQueryPtr());
346 }
347 
350  return(LeaseStatsQueryPtr());
351 }
352 
355  const SubnetID& /* last_subnet_id */) {
356  return(LeaseStatsQueryPtr());
357 }
358 
359 std::string
361  isc_throw(NotImplemented, "LeaseMgr::getDBVersion() called");
362 }
363 
364 void
366  std::string extended_info_tables;
367  try {
368  extended_info_tables = parameters.at("extended-info-tables");
369  } catch (const exception&) {
370  extended_info_tables = "false";
371  }
372  // If extended_info_tables is 'true' we will enable them.
373  if (extended_info_tables == "true") {
375  }
376 }
377 
378 bool
381  static OptionDefinitionPtr rai_def;
382 
383  bool changed = false;
384  if (!lease) {
385  return (changed);
386  }
387 
389  return (changed);
390  }
391 
392  ConstElementPtr user_context = lease->getContext();
393  if (!user_context) {
394  return (changed);
395  }
396 
397  if (!rai_def) {
400  }
401 
402  if (!rai_def) {
403  // The definition is set when libdhcp++ is loaded so it is impossible
404  // to not be able to get it... so should not happen!
405  isc_throw(Unexpected, "can't find RAI option definition?!");
406  }
407 
409  ConstElementPtr extended_info;
410  ElementPtr mutable_user_context;
411  ElementPtr mutable_isc;
412  string verifying = "";
413  bool removed_extended_info = false;
414 
415  try {
416  verifying = "user context";
417  if (user_context->getType() != Element::map) {
418  isc_throw(BadValue, "user context is not a map");
419  }
420  if (user_context->empty()) {
421  changed = true;
422  lease->setContext(ConstElementPtr());
423  return (changed);
424  }
425 
426  verifying = "isc";
427  isc = user_context->get("ISC");
428  if (!isc) {
429  return (changed);
430  }
431  mutable_user_context =
432  boost::const_pointer_cast<Element>(user_context);
433  if (!mutable_user_context) {
434  // Should not happen...
435  mutable_user_context = copy(user_context, 0);
436  lease->setContext(mutable_user_context);
437  }
438 
439  if (isc->getType() != Element::map) {
440  isc_throw(BadValue, "ISC entry is not a map");
441  }
442  if (isc->empty()) {
443  changed = true;
444  mutable_user_context->remove("ISC");
445  if (mutable_user_context->empty()) {
446  lease->setContext(ConstElementPtr());
447  }
448  return (changed);
449  }
450 
451  verifying = "relay-agent-info";
452  extended_info = isc->get("relay-agent-info");
453  if (!extended_info) {
454  return (changed);
455  }
456  mutable_isc = boost::const_pointer_cast<Element>(isc);
457  if (!mutable_isc) {
458  // Should not happen...
459  mutable_isc = copy(isc, 0);
460  mutable_user_context->set("ISC", mutable_isc);
461  }
462 
463  if (extended_info->getType() == Element::string) {
464  // Upgrade
465  changed = true;
466  ElementPtr upgraded = Element::createMap();
467  upgraded->set("sub-options", extended_info);
468  mutable_isc->set("relay-agent-info", upgraded);
469 
470  // Try to decode sub-options.
471  verifying = "rai";
472  string rai_hex = extended_info->stringValue();
473  vector<uint8_t> rai_data;
474  str::decodeFormattedHexString(rai_hex, rai_data);
475  OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4, rai_data));
476  if (!rai) {
477  isc_throw(BadValue, "can't create RAI option");
478  }
479 
480  OptionPtr remote_id = rai->getOption(RAI_OPTION_REMOTE_ID);
481  if (remote_id) {
482  vector<uint8_t> bytes = remote_id->toBinary(false);
483  if (bytes.size() > 0) {
484  upgraded->set("remote-id",
486  }
487  }
488 
489  OptionPtr relay_id = rai->getOption(RAI_OPTION_RELAY_ID);
490  if (relay_id) {
491  vector<uint8_t> bytes = relay_id->toBinary(false);
492  if (bytes.size() > 0) {
493  upgraded->set("relay-id",
495  }
496  }
497 
500  .arg(lease->addr_.toText());
501  return (changed);
502  } else if (extended_info->getType() != Element::map) {
503  mutable_isc->remove("relay-agent-info");
504  removed_extended_info = true;
505  isc_throw(BadValue, "relay-agent-info is not a map or a string");
506  }
507 
509  return (changed);
510  }
511 
512  // Try to decode sub-options.
513  ConstElementPtr sub_options = extended_info->get("sub-options");
514  if (sub_options) {
515  verifying = "sub-options";
516  if (sub_options->getType() != Element::string) {
517  mutable_isc->remove("relay-agent-info");
518  removed_extended_info = true;
519  isc_throw(BadValue, "sub-options is not a string");
520  }
521  string rai_hex = sub_options->stringValue();
522  vector<uint8_t> rai_data;
523  str::decodeFormattedHexString(rai_hex, rai_data);
524  }
525 
526  ConstElementPtr remote_id = extended_info->get("remote-id");
527  if (remote_id) {
528  verifying = "remote-id";
529  if (remote_id->getType() != Element::string) {
530  mutable_isc->remove("relay-agent-info");
531  removed_extended_info = true;
532  isc_throw(BadValue, "remote-id is not a string");
533  }
534  string remote_id_hex = remote_id->stringValue();
535  vector<uint8_t> remote_id_data;
536  encode::decodeHex(remote_id_hex, remote_id_data);
537  if (remote_id_data.empty()) {
538  mutable_isc->remove("relay-agent-info");
539  removed_extended_info = true;
540  isc_throw(BadValue, "remote-id is empty");
541  }
542  }
543 
544  ConstElementPtr relay_id = extended_info->get("relay-id");
545  if (relay_id) {
546  verifying = "relay-id";
547  if (relay_id->getType() != Element::string) {
548  mutable_isc->remove("relay-agent-info");
549  removed_extended_info = true;
550  isc_throw(BadValue, "relay-id is not a string");
551  }
552  string relay_id_hex = relay_id->stringValue();
553  vector<uint8_t> relay_id_data;
554  encode::decodeHex(relay_id_hex, relay_id_data);
555  if (relay_id_data.empty()) {
556  mutable_isc->remove("relay-agent-info");
557  removed_extended_info = true;
558  isc_throw(BadValue, "relay-id is empty");
559  }
560  }
561 
563  return (changed);
564  }
565 
566  verifying = "relay-agent-info";
567  for (auto elem : extended_info->mapValue()) {
568  if ((elem.first != "sub-options") &&
569  (elem.first != "remote-id") &&
570  (elem.first != "relay-id") &&
571  (elem.first != "comment")) {
572  mutable_isc->remove("relay-agent-info");
573  removed_extended_info = true;
574  isc_throw(BadValue, "spurious '" << elem.first <<
575  "' entry in relay-agent-info");
576  }
577  }
578 
579  return (changed);
580  } catch (const exception& ex) {
581  ostringstream err;
582  err << "in " << verifying << " a problem was found: " << ex.what();
584  .arg(lease->addr_.toText())
585  .arg(err.str());
586 
587  changed = true;
588  if (verifying == "user context") {
589  lease->setContext(ConstElementPtr());
590  } else if (verifying == "isc") {
591  mutable_user_context->remove("ISC");
592  if (mutable_user_context->empty()) {
593  lease->setContext(ConstElementPtr());
594  }
595  } else {
596  if (!removed_extended_info) {
597  mutable_isc->remove("relay-agent-info");
598  }
599  if (mutable_isc->empty()) {
600  mutable_user_context->remove("ISC");
601  if (mutable_user_context->empty()) {
602  lease->setContext(ConstElementPtr());
603  }
604  }
605  }
606  return (changed);
607  }
608 }
609 
610 bool
613  bool changed = false;
614  if (!lease) {
615  return (changed);
616  }
617 
619  return (changed);
620  }
621 
622  ConstElementPtr user_context = lease->getContext();
623  if (!user_context) {
624  return (changed);
625  }
626 
628  ConstElementPtr relay_info;
629  ElementPtr mutable_user_context;
630  ElementPtr mutable_isc;
631  string verifying = "";
632  bool removed_relay_info = false;
633  bool upgraded = false;
634  bool have_both = false;
635  int i = -1;
636 
637  try {
638  verifying = "user context";
639  if (user_context->getType() != Element::map) {
640  isc_throw(BadValue, "user context is not a map");
641  }
642  if (user_context->empty()) {
643  changed = true;
644  lease->setContext(ConstElementPtr());
645  return (changed);
646  }
647 
648  verifying = "isc";
649  isc = user_context->get("ISC");
650  if (!isc) {
651  return (changed);
652  }
653  mutable_user_context =
654  boost::const_pointer_cast<Element>(user_context);
655  if (!mutable_user_context) {
656  // Should not happen...
657  mutable_user_context = copy(user_context, 0);
658  lease->setContext(mutable_user_context);
659  }
660 
661  if (isc->getType() != Element::map) {
662  isc_throw(BadValue, "ISC entry is not a map");
663  }
664  if (isc->empty()) {
665  changed = true;
666  mutable_user_context->remove("ISC");
667  if (mutable_user_context->empty()) {
668  lease->setContext(ConstElementPtr());
669  }
670  return (changed);
671  }
672  mutable_isc = boost::const_pointer_cast<Element>(isc);
673  if (!mutable_isc) {
674  // Should not happen...
675  mutable_isc = copy(isc, 0);
676  mutable_user_context->set("ISC", mutable_isc);
677  }
678 
679  relay_info = mutable_isc->get("relays");
680  if (relay_info && isc->contains("relay-info")) {
681  changed = true;
682  mutable_isc->remove("relays");
683  have_both = true;
684  relay_info.reset();
685  }
686  if (relay_info) {
687  // Upgrade
688  changed = true;
689  upgraded = true;
690  verifying = "relays";
691  mutable_isc->set("relay-info", relay_info);
692  mutable_isc->remove("relays");
693 
694  if (relay_info->getType() != Element::list) {
695  mutable_isc->remove("relay-info");
696  removed_relay_info = true;
697  isc_throw(BadValue, "relays is not a list");
698  }
699  if (relay_info->empty()) {
700  mutable_isc->remove("relay-info");
701  removed_relay_info = true;
702  isc_throw(BadValue, "relays is empty");
703  }
704 
705  verifying = "relay";
706  for (i = 0; i < relay_info->size(); ++i) {
707  ElementPtr relay = relay_info->getNonConst(i);
708  if (!relay) {
709  mutable_isc->remove("relay-info");
710  removed_relay_info = true;
711  isc_throw(BadValue, "null relay#" << i);
712  }
713  if (relay->getType() != Element::map) {
714  mutable_isc->remove("relay-info");
715  removed_relay_info = true;
716  isc_throw(BadValue, "relay#" << i << " is not a map");
717  }
718 
719  // Try to decode options.
720  ConstElementPtr options = relay->get("options");
721  if (!options) {
722  continue;
723  }
724 
725  verifying = "options";
726  if (options->getType() != Element::string) {
727  mutable_isc->remove("relay-info");
728  removed_relay_info = true;
729  isc_throw(BadValue, "options is not a string");
730  }
731  string options_hex = options->stringValue();
732  vector<uint8_t> options_data;
733  str::decodeFormattedHexString(options_hex, options_data);
734  OptionCollection opts;
735  LibDHCP::unpackOptions6(options_data, DHCP6_OPTION_SPACE, opts);
736 
737  auto remote_id_it = opts.find(D6O_REMOTE_ID);
738  if (remote_id_it != opts.end()) {
739  OptionPtr remote_id = remote_id_it->second;
740  if (remote_id) {
741  vector<uint8_t> bytes = remote_id->toBinary(false);
742  if (bytes.size() > 0) {
743  relay->set("remote-id",
745  }
746  }
747  }
748 
749  auto relay_id_it = opts.find(D6O_RELAY_ID);
750  if (relay_id_it != opts.end()) {
751  OptionPtr relay_id = relay_id_it->second;
752  if (relay_id) {
753  vector<uint8_t> bytes = relay_id->toBinary(false);
754  if (bytes.size() > 0) {
755  relay->set("relay-id",
757  }
758  }
759  }
760  }
761  }
762 
763  verifying = (upgraded ? "relays" : "relay-info");
764  i = -1;
765  relay_info = mutable_isc->get("relay-info");
766  if (!relay_info) {
767  return (changed);
768  }
769  if (!upgraded && (relay_info->getType() != Element::list)) {
770  mutable_isc->remove("relay-info");
771  removed_relay_info = true;
772  isc_throw(BadValue, "relay-info is not a list");
773  }
774  if (!upgraded && relay_info->empty()) {
775  mutable_isc->remove("relay-info");
776  removed_relay_info = true;
777  isc_throw(BadValue, "relay-info is empty");
778  }
779 
780  verifying = "relay";
781  for (i = 0; i < relay_info->size(); ++i) {
782  ElementPtr relay = relay_info->getNonConst(i);
783  if (!upgraded && !relay) {
784  mutable_isc->remove("relay-info");
785  removed_relay_info = true;
786  isc_throw(BadValue, "null relay#" << i);
787  }
788  if (!upgraded && (relay->getType() != Element::map)) {
789  mutable_isc->remove("relay-info");
790  removed_relay_info = true;
791  isc_throw(BadValue, "relay#" << i << " is not a map");
792  }
793 
794  ConstElementPtr options = relay->get("options");
795  if (!upgraded && options) {
796  // Try to decode options.
797  verifying = "options";
798  if (options->getType() != Element::string) {
799  mutable_isc->remove("relay-info");
800  removed_relay_info = true;
801  isc_throw(BadValue, "options is not a string");
802  }
803  string options_hex = options->stringValue();
804  vector<uint8_t> options_data;
805  str::decodeFormattedHexString(options_hex, options_data);
806  OptionCollection opts;
807  LibDHCP::unpackOptions6(options_data, DHCP6_OPTION_SPACE, opts);
808  }
810  continue;
811  }
812 
813  verifying = "link";
814  ConstElementPtr link_addr = relay->get("link");
815  if (!link_addr) {
816  mutable_isc->remove("relay-info");
817  removed_relay_info = true;
818  isc_throw(BadValue, "no link");
819  }
820  if (link_addr->getType() != Element::string) {
821  mutable_isc->remove("relay-info");
822  removed_relay_info = true;
823  isc_throw(BadValue, "link is not a string");
824  }
825  IOAddress laddr(link_addr->stringValue());
826  if (!laddr.isV6()) {
827  mutable_isc->remove("relay-info");
828  removed_relay_info = true;
829  isc_throw(BadValue, "link is not an IPv6 address");
830  }
831 
832  ConstElementPtr remote_id = relay->get("remote-id");
833  if (!upgraded && remote_id) {
834  verifying = "remote-id";
835  if (remote_id->getType() != Element::string) {
836  mutable_isc->remove("relay-info");
837  removed_relay_info = true;
838  isc_throw(BadValue, "remote-id is not a string");
839  }
840  string remote_id_hex = remote_id->stringValue();
841  vector<uint8_t> remote_id_data;
842  encode::decodeHex(remote_id_hex, remote_id_data);
843  if (remote_id_data.empty()) {
844  mutable_isc->remove("relay-info");
845  removed_relay_info = true;
846  isc_throw(BadValue, "remote-id is empty");
847  }
848  }
849 
850  ConstElementPtr relay_id = relay->get("relay-id");
851  if (!upgraded && relay_id) {
852  verifying = "relay-id";
853  if (relay_id->getType() != Element::string) {
854  mutable_isc->remove("relay-info");
855  removed_relay_info = true;
856  isc_throw(BadValue, "relay-id is not a string");
857  }
858  string relay_id_hex = relay_id->stringValue();
859  vector<uint8_t> relay_id_data;
860  encode::decodeHex(relay_id_hex, relay_id_data);
861  if (relay_id_data.empty()) {
862  mutable_isc->remove("relay-info");
863  removed_relay_info = true;
864  isc_throw(BadValue, "relay-id is empty");
865  }
866  }
867 
869  continue;
870  }
871 
872  verifying = "peer";
873  ConstElementPtr peer_addr = relay->get("peer");
874  if (!peer_addr) {
875  mutable_isc->remove("relay-info");
876  removed_relay_info = true;
877  isc_throw(BadValue, "no peer");
878  }
879  if (peer_addr->getType() != Element::string) {
880  mutable_isc->remove("relay-info");
881  removed_relay_info = true;
882  isc_throw(BadValue, "peer is not a string");
883  }
884  IOAddress paddr(peer_addr->stringValue());
885  if (!paddr.isV6()) {
886  mutable_isc->remove("relay-info");
887  removed_relay_info = true;
888  isc_throw(BadValue, "peer is not an IPv6 address");
889  }
890 
891  verifying = "hop";
892  ConstElementPtr hop = relay->get("hop");
893  if (!hop) {
894  mutable_isc->remove("relay-info");
895  removed_relay_info = true;
896  isc_throw(BadValue, "no hop");
897  }
898  if (hop->getType() != Element::integer) {
899  mutable_isc->remove("relay-info");
900  removed_relay_info = true;
901  isc_throw(BadValue, "hop is not an integer");
902  }
903 
904  verifying = (upgraded ? "relays" : "relay-info");
905  for (auto elem : relay->mapValue()) {
906  if ((elem.first != "hop") &&
907  (elem.first != "link") &&
908  (elem.first != "peer") &&
909  (elem.first != "options") &&
910  (elem.first != "remote-id") &&
911  (elem.first != "relay-id") &&
912  (elem.first != "comment")) {
913  mutable_isc->remove("relay-info");
914  removed_relay_info = true;
915  isc_throw(BadValue, "spurious '" << elem.first << "' entry");
916  }
917  }
918  }
919 
920  if (upgraded) {
923  .arg(lease->addr_.toText());
924  }
925 
926  return (changed);
927  } catch (const exception& ex) {
928  ostringstream err;
929  err << "in " << verifying;
930  if (i >= 0) {
931  err << " [relay#" << i << "]";
932  }
933  err << " a problem was found: " << ex.what();
935  .arg(lease->addr_.toText())
936  .arg(err.str());
937 
938  changed = true;
939  have_both = !have_both;
940  if (verifying == "user context") {
941  lease->setContext(ConstElementPtr());
942  } else if (verifying == "isc") {
943  mutable_user_context->remove("ISC");
944  if (mutable_user_context->empty()) {
945  lease->setContext(ConstElementPtr());
946  }
947  } else {
948  if (!removed_relay_info) {
949  mutable_isc->remove("relay-info");
950  }
951  if (mutable_isc->empty()) {
952  mutable_user_context->remove("ISC");
953  if (mutable_user_context->empty()) {
954  lease->setContext(ConstElementPtr());
955  }
956  }
957  }
958  return (changed);
959  }
960 }
961 
962 void
964  bool ignore_errors) {
965  if (!lease) {
966  return;
967  }
968 
969  ConstElementPtr user_context = lease->getContext();
970  if (!user_context) {
971  return;
972  }
973  if (user_context->getType() != Element::map) {
974  if (ignore_errors) {
975  return;
976  }
977  isc_throw(BadValue, "user context is not a map");
978  }
979  if (user_context->empty()) {
980  return;
981  }
982 
983  ConstElementPtr isc = user_context->get("ISC");
984  if (!isc) {
985  return;
986  }
987  if (isc->getType() != Element::map) {
988  if (ignore_errors) {
989  return;
990  }
991  isc_throw(BadValue, "ISC entry is not a map");
992  }
993  if (isc->empty()) {
994  return;
995  }
996 
997  ConstElementPtr extended_info = isc->get("relay-agent-info");
998  if (!extended_info) {
999  return;
1000  }
1001  if (extended_info->getType() != Element::map) {
1002  if (ignore_errors) {
1003  return;
1004  }
1005  isc_throw(BadValue, "relay-agent-info is not a map");
1006  }
1007  if (extended_info->empty()) {
1008  return;
1009  }
1010 
1011  ConstElementPtr relay_id = extended_info->get("relay-id");
1012  if (relay_id) {
1013  if (relay_id->getType() == Element::string) {
1014  vector<uint8_t> bytes;
1015  try {
1016  encode::decodeHex(relay_id->stringValue(), bytes);
1017  } catch (...) {
1018  // Decode failed
1019  if (!ignore_errors) {
1020  throw;
1021  }
1022  }
1023  lease->relay_id_ = bytes;
1024  } else if (!ignore_errors) {
1025  isc_throw(BadValue, "relay-id entry is not a string");
1026  }
1027  }
1028 
1029  ConstElementPtr remote_id = extended_info->get("remote-id");
1030  if (remote_id) {
1031  if (remote_id->getType() == Element::string) {
1032  vector<uint8_t> bytes;
1033  try {
1034  encode::decodeHex(remote_id->stringValue(), bytes);
1035  } catch (...) {
1036  // Decode failed
1037  if (!ignore_errors) {
1038  throw;
1039  }
1040  }
1041  lease->remote_id_ = bytes;
1042  } else if (!ignore_errors) {
1043  isc_throw(BadValue, "remote-id entry is not a string");
1044  }
1045  }
1046 }
1047 
1048 bool
1050 
1051  bool added = false;
1052  if (!lease) {
1053  return (added);
1054  }
1055 
1056  ConstElementPtr user_context = lease->getContext();
1057  if (!user_context || (user_context->getType() != Element::map) ||
1058  user_context->empty()) {
1059  return (added);
1060  }
1061 
1062  ConstElementPtr isc = user_context->get("ISC");
1063  if (!isc || (isc->getType() != Element::map) || isc->empty()) {
1064  return (added);
1065  }
1066 
1067  ConstElementPtr relay_info = isc->get("relay-info");
1068  if (!relay_info || (relay_info->getType() != Element::list) ||
1069  relay_info->empty()) {
1070  return (added);
1071  }
1072 
1073  for (int i = 0; i < relay_info->size(); ++i) {
1074  ConstElementPtr relay = relay_info->get(i);
1075  if (!relay || (relay->getType() != Element::map) || relay->empty()) {
1076  continue;
1077  }
1078  try {
1079  ConstElementPtr relay_id = relay->get("relay-id");
1080  if (relay_id) {
1081  string relay_id_hex = relay_id->stringValue();
1082  vector<uint8_t> relay_id_data;
1083  encode::decodeHex(relay_id_hex, relay_id_data);
1084  if (relay_id_data.empty()) {
1085  continue;
1086  }
1087  addRelayId6(lease->addr_, relay_id_data);
1088  added = true;
1089  }
1090 
1091  ConstElementPtr remote_id = relay->get("remote-id");
1092  if (remote_id) {
1093  string remote_id_hex = remote_id->stringValue();
1094  vector<uint8_t> remote_id_data;
1095  encode::decodeHex(remote_id_hex, remote_id_data);
1096  if (remote_id_data.empty()) {
1097  continue;
1098  }
1099  addRemoteId6(lease->addr_, remote_id_data);
1100  added = true;
1101  }
1102  } catch (const exception&) {
1103  continue;
1104  }
1105  }
1106  return (added);
1107 }
1108 
1109 } // namespace isc::dhcp
1110 } // namespace isc
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 when a function is not implemented.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A generic exception that is thrown when an unexpected error condition occurs.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:241
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:291
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Multiple lease records found where one expected.
Definition: db_exceptions.h:16
ExtendedInfoSanity
Values for extended info sanity checks done for leases.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
Holds DUID (DHCPv6 Unique Identifier)
Definition: duid.h:27
std::string toText() const
Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
Definition: duid.cc:75
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery6(const SubnetID &subnet_id)
Creates and runs the IPv6 lease stats query for a single subnet.
Definition: lease_mgr.cc:349
virtual Lease6Collection getLeases6() const =0
Returns all IPv6 leases.
void recountLeaseStats6()
Recalculates per-subnet and global stats for IPv6 leases.
Definition: lease_mgr.cc:221
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:611
virtual void addRelayId6(const isc::asiolink::IOAddress &lease_addr, const std::vector< uint8_t > &relay_id)=0
Add lease6 extended info into by-relay-id table.
virtual void setExtendedInfoTablesEnabled(const bool enabled)
Extended information / Bulk Lease Query shared interface.
Definition: lease_mgr.h:991
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery4(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id)
Creates and runs the IPv4 lease stats query for a single subnet.
Definition: lease_mgr.cc:210
void recountLeaseStats4()
Recalculates per-subnet and global stats for IPv4 leases.
Definition: lease_mgr.cc:73
static std::string getDBVersion()
Class method to return extended version info This class method must be redeclared and redefined in de...
Definition: lease_mgr.cc:360
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery6(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id)
Creates and runs the IPv6 lease stats query for a single subnet.
Definition: lease_mgr.cc:354
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery4(const SubnetID &subnet_id)
Creates and runs the IPv4 lease stats query for a single subnet.
Definition: lease_mgr.cc:205
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const =0
Returns existing IPv6 lease for a given IPv6 address.
virtual LeaseStatsQueryPtr startLeaseStatsQuery4()
Creates and runs the IPv4 lease stats query for all subnets.
Definition: lease_mgr.cc:200
static void extractLease4ExtendedInfo(const Lease4Ptr &lease, bool ignore_errors=true)
Extract relay and remote identifiers from the extended info.
Definition: lease_mgr.cc:963
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:379
virtual void addRemoteId6(const isc::asiolink::IOAddress &lease_addr, const std::vector< uint8_t > &remote_id)=0
Add lease6 extended info into by-remote-id table.
virtual bool addExtendedInfo6(const Lease6Ptr &lease)
Extract extended info from a lease6 and add it into tables.
Definition: lease_mgr.cc:1049
virtual LeaseStatsQueryPtr startLeaseStatsQuery6()
Creates and runs the IPv6 lease stats query for all subnets.
Definition: lease_mgr.cc:344
const size_t page_size_
Holds page size.
Definition: lease_mgr.h:56
SubnetID first_subnet_id_
First (or only) subnet_id in the selection criteria.
Definition: lease_mgr.h:195
SubnetID last_subnet_id_
Last subnet_id in the selection criteria when a range is given.
Definition: lease_mgr.h:202
virtual bool getNextRow(LeaseStatsRow &row)
Fetches the next row of data.
Definition: lease_mgr.cc:216
LeaseStatsQuery()
Default constructor The query created will return statistics for all subnets.
Definition: lease_mgr.cc:167
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition: libdhcp++.cc:124
static size_t unpackOptions6(const OptionBuffer &buf, const std::string &option_space, isc::dhcp::OptionCollection &options, size_t *relay_msg_offset=0, size_t *relay_msg_len=0)
Parses provided buffer as DHCPv6 options and creates Option objects.
Definition: libdhcp++.cc:309
Option with defined data fields represented as buffers that can be accessed using data field index.
Definition: option_custom.h:32
@ D6O_REMOTE_ID
Definition: dhcp6.h:57
@ D6O_RELAY_ID
Definition: dhcp6.h:73
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
An abstract API for lease database.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1360
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
const isc::log::MessageID DHCPSRV_LEASE6_EXTENDED_INFO_UPGRADED
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition: lease.h:509
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition: lease.h:677
boost::shared_ptr< LeaseStatsQuery > LeaseStatsQueryPtr
Defines a pointer to a LeaseStatsQuery.
Definition: lease_mgr.h:210
boost::multi_index_container< Subnet6Ptr, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetSubnetIdIndexTag >, boost::multi_index::const_mem_fun< Subnet, SubnetID, &Subnet::getID > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetPrefixIndexTag >, boost::multi_index::const_mem_fun< Subnet, std::string, &Subnet::toText > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > >> Subnet6Collection
A collection of Subnet6 objects.
Definition: subnet.h:946
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
boost::multi_index_container< Subnet4Ptr, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetSubnetIdIndexTag >, boost::multi_index::const_mem_fun< Subnet, SubnetID, &Subnet::getID > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetPrefixIndexTag >, boost::multi_index::const_mem_fun< Subnet, std::string, &Subnet::toText > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetServerIdIndexTag >, boost::multi_index::const_mem_fun< Network4, asiolink::IOAddress, &Network4::getServerId > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > >> Subnet4Collection
A collection of Subnet4 objects.
Definition: subnet.h:875
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition: subnet_id.h:25
@ RAI_OPTION_RELAY_ID
Definition: dhcp4.h:275
@ RAI_OPTION_REMOTE_ID
Definition: dhcp4.h:265
const isc::log::MessageID DHCPSRV_LEASE4_EXTENDED_INFO_SANITY_FAIL
const isc::log::MessageID DHCPSRV_LEASE4_EXTENDED_INFO_UPGRADED
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:284
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const isc::log::MessageID DHCPSRV_LEASE6_EXTENDED_INFO_SANITY_FAIL
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 ('hex') format.
Definition: base_n.cc:483
void decodeHex(const string &input, vector< uint8_t > &result)
Decode a text encoded in the base16 ('hex') format into the original data.
Definition: base_n.cc:488
void decodeFormattedHexString(const std::string &hex_string, std::vector< uint8_t > &binary)
Converts a formatted string of hexadecimal digits into a vector.
Definition: strutil.cc:273
Definition: edns.h:19
Defines the logger used by the top-level component of kea-lfc.
#define DHCP4_OPTION_SPACE
global std option spaces
#define DHCP6_OPTION_SPACE
Contains a single row of lease statistical data.
Definition: lease_mgr.h:64
int64_t state_count_
state_count The count of leases in the lease state
Definition: lease_mgr.h:123
uint32_t lease_state_
The lease_state to which the count applies.
Definition: lease_mgr.h:121
SubnetID subnet_id_
The subnet ID to which this data applies.
Definition: lease_mgr.h:117
Lease::Type lease_type_
The lease_type to which the count applies.
Definition: lease_mgr.h:119
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition: lease.h:69
static const uint32_t STATE_DECLINED
Declined lease.
Definition: lease.h:72
Type
Type of lease or pool.
Definition: lease.h:46
@ 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