Kea  2.3.1-git
client_class_def.cc
Go to the documentation of this file.
1 // Copyright (C) 2015-2022 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 <eval/dependency.h>
11 #include <dhcpsrv/cfgmgr.h>
13 #include <boost/foreach.hpp>
14 
15 #include <queue>
16 
17 using namespace isc::data;
18 
19 namespace isc {
20 namespace dhcp {
21 
22 //********** ClientClassDef ******************//
23 
24 ClientClassDef::ClientClassDef(const std::string& name,
25  const ExpressionPtr& match_expr,
26  const CfgOptionPtr& cfg_option)
28  match_expr_(match_expr), required_(false), depend_on_known_(false),
29  cfg_option_(cfg_option), next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
30  valid_(), preferred_() {
31 
32  // Name can't be blank
33  if (name_.empty()) {
34  isc_throw(BadValue, "Client Class name cannot be blank");
35  }
36 
37  // We permit an empty expression for now. This will likely be useful
38  // for automatic classes such as vendor class.
39  // For classes without options, make sure we have an empty collection
40  if (!cfg_option_) {
41  cfg_option_.reset(new CfgOption());
42  }
43 }
44 
46  : UserContext(rhs), CfgToElement(rhs), StampedElement(rhs), name_(rhs.name_),
47  match_expr_(ExpressionPtr()), test_(rhs.test_), required_(rhs.required_),
48  depend_on_known_(rhs.depend_on_known_), cfg_option_(new CfgOption()),
49  next_server_(rhs.next_server_), sname_(rhs.sname_),
50  filename_(rhs.filename_), valid_(rhs.valid_), preferred_(rhs.preferred_) {
51 
52  if (rhs.match_expr_) {
53  match_expr_.reset(new Expression());
54  *match_expr_ = *(rhs.match_expr_);
55  }
56 
57  if (rhs.cfg_option_def_) {
58  cfg_option_def_.reset(new CfgOptionDef());
59  rhs.cfg_option_def_->copyTo(*cfg_option_def_);
60  }
61 
62  if (rhs.cfg_option_) {
63  rhs.cfg_option_->copyTo(*cfg_option_);
64  }
65 }
66 
68 }
69 
70 std::string
72  return (name_);
73 }
74 
75 void
76 ClientClassDef::setName(const std::string& name) {
77  name_ = name;
78 }
79 
80 const ExpressionPtr&
82  return (match_expr_);
83 }
84 
85 void
87  match_expr_ = match_expr;
88 }
89 
90 std::string
92  return (test_);
93 }
94 
95 void
96 ClientClassDef::setTest(const std::string& test) {
97  test_ = test;
98 }
99 
100 bool
102  return (required_);
103 }
104 
105 void
107  required_ = required;
108 }
109 
110 bool
112  return (depend_on_known_);
113 }
114 
115 void
116 ClientClassDef::setDependOnKnown(bool depend_on_known) {
117  depend_on_known_ = depend_on_known;
118 }
119 
120 const CfgOptionDefPtr&
122  return (cfg_option_def_);
123 }
124 
125 void
127  cfg_option_def_ = cfg_option_def;
128 }
129 
130 const CfgOptionPtr&
132  return (cfg_option_);
133 }
134 
135 void
137  cfg_option_ = cfg_option;
138 }
139 
140 bool
141 ClientClassDef::dependOnClass(const std::string& name) const {
142  return (isc::dhcp::dependOnClass(match_expr_, name));
143 }
144 
145 bool
147  return ((name_ == other.name_) &&
148  ((!match_expr_ && !other.match_expr_) ||
149  (match_expr_ && other.match_expr_ &&
150  (*match_expr_ == *(other.match_expr_)))) &&
151  ((!cfg_option_ && !other.cfg_option_) ||
152  (cfg_option_ && other.cfg_option_ &&
153  (*cfg_option_ == *other.cfg_option_))) &&
154  ((!cfg_option_def_ && !other.cfg_option_def_) ||
155  (cfg_option_def_ && other.cfg_option_def_ &&
156  (*cfg_option_def_ == *other.cfg_option_def_))) &&
157  (required_ == other.required_) &&
158  (depend_on_known_ == other.depend_on_known_) &&
159  (next_server_ == other.next_server_) &&
160  (sname_ == other.sname_) &&
161  (filename_ == other.filename_));
162 }
163 
166  uint16_t family = CfgMgr::instance().getFamily();
167  ElementPtr result = Element::createMap();
168  // Set user-context
169  contextToElement(result);
170  // Set name
171  result->set("name", Element::create(name_));
172  // Set original match expression (empty string won't parse)
173  if (!test_.empty()) {
174  result->set("test", Element::create(test_));
175  }
176  // Set only-if-required
177  if (required_) {
178  result->set("only-if-required", Element::create(required_));
179  }
180  // Set option-def (used only by DHCPv4)
181  if (cfg_option_def_ && (family == AF_INET)) {
182  result->set("option-def", cfg_option_def_->toElement());
183  }
184  // Set option-data
185  result->set("option-data", cfg_option_->toElement());
186 
187  if (family == AF_INET) {
188  // V4 only
189  // Set next-server
190  result->set("next-server", Element::create(next_server_.toText()));
191  // Set server-hostname
192  result->set("server-hostname", Element::create(sname_));
193  // Set boot-file-name
194  result->set("boot-file-name", Element::create(filename_));
195  } else {
196  // V6 only
197  // Set preferred-lifetime
198  if (!preferred_.unspecified()) {
199  result->set("preferred-lifetime",
200  Element::create(static_cast<long long>(preferred_.get())));
201  }
202 
203  if (preferred_.getMin() < preferred_.get()) {
204  result->set("min-preferred-lifetime",
205  Element::create(static_cast<long long>(preferred_.getMin())));
206  }
207 
208  if (preferred_.getMax() > preferred_.get()) {
209  result->set("max-preferred-lifetime",
210  Element::create(static_cast<long long>(preferred_.getMax())));
211  }
212  }
213 
214  // Set valid-lifetime
215  if (!valid_.unspecified()) {
216  result->set("valid-lifetime",
217  Element::create(static_cast<long long>(valid_.get())));
218 
219  if (valid_.getMin() < valid_.get()) {
220  result->set("min-valid-lifetime",
221  Element::create(static_cast<long long>(valid_.getMin())));
222  }
223 
224  if (valid_.getMax() > valid_.get()) {
225  result->set("max-valid-lifetime",
226  Element::create(static_cast<long long>(valid_.getMax())));
227  }
228  }
229 
230  return (result);
231 }
232 
233 std::ostream& operator<<(std::ostream& os, const ClientClassDef& x) {
234  os << "ClientClassDef:" << x.getName();
235  return (os);
236 }
237 
238 //********** ClientClassDictionary ******************//
239 
241  : map_(new ClientClassDefMap()), list_(new ClientClassDefList()) {
242 }
243 
245  : map_(new ClientClassDefMap()), list_(new ClientClassDefList()) {
246  BOOST_FOREACH(ClientClassDefPtr cclass, *(rhs.list_)) {
247  ClientClassDefPtr copy(new ClientClassDef(*cclass));
248  addClass(copy);
249  }
250 }
251 
253 }
254 
255 void
256 ClientClassDictionary::addClass(const std::string& name,
257  const ExpressionPtr& match_expr,
258  const std::string& test,
259  bool required,
260  bool depend_on_known,
261  const CfgOptionPtr& cfg_option,
262  CfgOptionDefPtr cfg_option_def,
263  ConstElementPtr user_context,
264  asiolink::IOAddress next_server,
265  const std::string& sname,
266  const std::string& filename,
267  const util::Triplet<uint32_t>& valid,
268  const util::Triplet<uint32_t>& preferred) {
269  ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
270  cclass->setTest(test);
271  cclass->setRequired(required);
272  cclass->setDependOnKnown(depend_on_known);
273  cclass->setCfgOptionDef(cfg_option_def);
274  cclass->setContext(user_context),
275  cclass->setNextServer(next_server);
276  cclass->setSname(sname);
277  cclass->setFilename(filename);
278  cclass->setValid(valid);
279  cclass->setPreferred(preferred);
280  addClass(cclass);
281 }
282 
283 void
285  if (!class_def) {
286  isc_throw(BadValue, "ClientClassDictionary::addClass "
287  " - class definition cannot be null");
288  }
289 
290  if (findClass(class_def->getName())) {
291  isc_throw(DuplicateClientClassDef, "Client Class: "
292  << class_def->getName() << " has already been defined");
293  }
294 
295  list_->push_back(class_def);
296  (*map_)[class_def->getName()] = class_def;
297 }
298 
300 ClientClassDictionary::findClass(const std::string& name) const {
301  ClientClassDefMap::iterator it = map_->find(name);
302  if (it != map_->end()) {
303  return (*it).second;
304  }
305 
306  return (ClientClassDefPtr());
307 }
308 
309 void
310 ClientClassDictionary::removeClass(const std::string& name) {
311  for (ClientClassDefList::iterator this_class = list_->begin();
312  this_class != list_->end(); ++this_class) {
313  if ((*this_class)->getName() == name) {
314  list_->erase(this_class);
315  break;
316  }
317  }
318  map_->erase(name);
319 }
320 
321 void
323  // Class id equal to 0 means it wasn't set.
324  if (id == 0) {
325  return;
326  }
327  for (ClientClassDefList::iterator this_class = list_->begin();
328  this_class != list_->end(); ++this_class) {
329  if ((*this_class)->getId() == id) {
330  map_->erase((*this_class)->getName());
331  list_->erase(this_class);
332  break;
333  }
334  }
335 }
336 
339  return (list_);
340 }
341 
342 bool
344  return (list_->empty());
345 }
346 
347 bool
348 ClientClassDictionary::dependOnClass(const std::string& name,
349  std::string& dependent_class) const {
350  // Skip previous classes as they should not depend on name.
351  bool found = false;
352  for (ClientClassDefList::iterator this_class = list_->begin();
353  this_class != list_->end(); ++this_class) {
354  if (found) {
355  if ((*this_class)->dependOnClass(name)) {
356  dependent_class = (*this_class)->getName();
357  return (true);
358  }
359  } else {
360  if ((*this_class)->getName() == name) {
361  found = true;
362  }
363  }
364  }
365  return (false);
366 }
367 
368 bool
370  if (list_->size() != other.list_->size()) {
371  return (false);
372  }
373 
374  ClientClassDefList::const_iterator this_class = list_->cbegin();
375  ClientClassDefList::const_iterator other_class = other.list_->cbegin();
376  while (this_class != list_->cend() &&
377  other_class != other.list_->cend()) {
378  if (!*this_class || !*other_class ||
379  **this_class != **other_class) {
380  return false;
381  }
382 
383  ++this_class;
384  ++other_class;
385  }
386 
387  return (true);
388 }
389 
390 void
392  std::queue<ExpressionPtr> expressions;
393  for (auto c : *list_) {
394  if (!c->getTest().empty()) {
395  ExpressionPtr match_expr = boost::make_shared<Expression>();
396  ExpressionParser parser;
397  parser.parse(match_expr, Element::create(c->getTest()), family);
398  expressions.push(match_expr);
399  }
400  }
401  // All expressions successfully initialized. Let's set them for the
402  // client classes in the dictionary.
403  for (auto c : *list_) {
404  if (!c->getTest().empty()) {
405  c->setMatchExpr(expressions.front());
406  expressions.pop();
407  }
408  }
409 }
410 
411 void
413  for (auto c : *list_) {
414  // If the class has no options, skip it.
415  CfgOptionPtr class_options = c->getCfgOption();
416  if (!class_options || class_options->empty()) {
417  continue;
418  }
419 
420  // If the class has no option definitions, use the set
421  // of definitions we were given as is to create its
422  // options.
423  if (!c->getCfgOptionDef()) {
424  class_options->createOptions(external_defs);
425  } else {
426  // Class has its own option definitions, we need a
427  // composite set of definitions to recreate its options.
428  // We make copies of both sets of definitions, then merge
429  // the external defs copy into the class defs copy.
430  // We do this because merging actually effects both sets
431  // of definitions and we cannot alter either set.
432  // Seed the composite set with the class's definitions.
433  CfgOptionDefPtr composite_defs(new CfgOptionDef());
434  c->getCfgOptionDef()->copyTo(*composite_defs);
435 
436  // Make a copy of the external definitions and
437  // merge those into the composite set. This should give
438  // us a set of options with class definitions taking
439  // precedence.
440  CfgOptionDefPtr external_defs_copy(new CfgOptionDef());
441  external_defs->copyTo(*external_defs_copy);
442  composite_defs->merge(*external_defs_copy);
443 
444  // Now create the class options using the composite
445  // set of definitions.
446  class_options->createOptions(composite_defs);
447  }
448  }
449 }
450 
453  ElementPtr result = Element::createList();
454  // Iterate on the map
455  for (ClientClassDefList::const_iterator this_class = list_->begin();
456  this_class != list_->cend(); ++this_class) {
457  result->add((*this_class)->toElement());
458  }
459  return (result);
460 }
461 
464  if (this != &rhs) {
465  list_->clear();
466  map_->clear();
467  for (auto cclass : *(rhs.list_)) {
468  ClientClassDefPtr copy(new ClientClassDef(*cclass));
469  addClass(copy);
470  }
471  }
472  return (*this);
473 }
474 
476 std::list<std::string>
478  // DROP is not in this list because it is special but not built-in.
479  // In fact DROP is set from an expression as callouts can drop
480  // directly the incoming packet. The expression must not depend on
481  // KNOWN/UNKNOWN which are set far after the drop point.
482  // SKIP_DDNS, used by DDNS-tuning is also omitted from this list
483  // so users may assign it a test expression.
484  "ALL", "KNOWN", "UNKNOWN", "BOOTP"
485 };
486 
487 std::list<std::string>
489  "VENDOR_CLASS_", "HA_", "AFTER_", "EXTERNAL_"
490 };
491 
492 bool
493 isClientClassBuiltIn(const ClientClass& client_class) {
494  for (std::list<std::string>::const_iterator bn = builtinNames.cbegin();
495  bn != builtinNames.cend(); ++bn) {
496  if (client_class == *bn) {
497  return true;
498  }
499  }
500 
501  for (std::list<std::string>::const_iterator bt = builtinPrefixes.cbegin();
502  bt != builtinPrefixes.cend(); ++bt) {
503  if (client_class.size() <= bt->size()) {
504  continue;
505  }
506  auto mis = std::mismatch(bt->cbegin(), bt->cend(), client_class.cbegin());
507  if (mis.first == bt->cend()) {
508  return true;
509  }
510  }
511 
512  return false;
513 }
514 
515 bool
517  bool& depend_on_known,
518  const ClientClass& client_class) {
519  // First check built-in classes
520  if (isClientClassBuiltIn(client_class)) {
521  // Check direct dependency on [UN]KNOWN
522  if ((client_class == "KNOWN") || (client_class == "UNKNOWN")) {
523  depend_on_known = true;
524  }
525  return (true);
526  }
527 
528  // Second check already defined, i.e. in the dictionary
529  ClientClassDefPtr def = class_dictionary->findClass(client_class);
530  if (def) {
531  // Check indirect dependency on [UN]KNOWN
532  if (def->getDependOnKnown()) {
533  depend_on_known = true;
534  }
535  return (true);
536  }
537 
538  // Not defined...
539  return (false);
540 }
541 
542 } // namespace isc::dhcp
543 } // namespace isc
void setMatchExpr(const ExpressionPtr &match_expr)
Sets the class&#39;s match expression.
bool dependOnClass(const std::string &name, std::string &dependent_class) const
Checks direct dependency.
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:136
This class represents configuration element which is associated with database identifier, modification timestamp and servers.
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:745
const CfgOptionDefPtr & getCfgOptionDef() const
Fetches the class&#39;s option definitions.
void addClass(const std::string &name, const ExpressionPtr &match_expr, const std::string &test, bool required, bool depend_on_known, const CfgOptionPtr &options, CfgOptionDefPtr defs=CfgOptionDefPtr(), isc::data::ConstElementPtr user_context=isc::data::ConstElementPtr(), asiolink::IOAddress next_server=asiolink::IOAddress("0.0.0.0"), const std::string &sname=std::string(), const std::string &filename=std::string(), const util::Triplet< uint32_t > &valid=util::Triplet< uint32_t >(), const util::Triplet< uint32_t > &preferred=util::Triplet< uint32_t >())
Adds a new class to the list.
void setDependOnKnown(bool depend_on_known)
Sets the depend on known flag aka use host flag.
T getMin() const
Returns a minimum allowed value.
Definition: triplet.h:85
bool dependOnClass(const std::string &name) const
Checks direct dependency.
ClientClassDefPtr findClass(const std::string &name) const
Fetches the class definition for a given class name.
Base class for user context.
Definition: user_context.h:22
void setRequired(bool required)
Sets the only if required flag.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
const ClientClassDefListPtr & getClasses() const
Fetches the dictionary&#39;s list of classes.
std::unordered_map< std::string, ClientClassDefPtr > ClientClassDefMap
Defines a map of ClientClassDef&#39;s, keyed by the class name.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
std::string getName() const
Fetches the class&#39;s name.
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
void setTest(const std::string &test)
Sets the class&#39;s original match expression.
void parse(ExpressionPtr &expression, isc::data::ConstElementPtr expression_cfg, uint16_t family, isc::eval::EvalContext::CheckDefined check_defined=isc::eval::EvalContext::acceptAll)
Parses an expression configuration element into an Expression.
bool equals(const ClientClassDef &other) const
Compares two ClientClassDef objects for equality.
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:291
Maintains a list of ClientClassDef&#39;s.
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:286
bool isClientClassDefined(ClientClassDictionaryPtr &class_dictionary, bool &depend_on_known, const ClientClass &client_class)
Check if a client class name is already defined, i.e.
ClientClassDictionary & operator=(const ClientClassDictionary &rhs)
Copy assignment operator.
const CfgOptionPtr & getCfgOption() const
Fetches the class&#39;s option collection.
Parser for a logical expression.
T getMax() const
Returns a maximum allowed value.
Definition: triplet.h:112
bool empty() const
Checks if the class dictionary is empty.
#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...
Represents option data configuration for the DHCP server.
Definition: cfg_option.h:318
Parsers for client class definitions.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1360
Error that occurs when an attempt is made to add a duplicate class to a class dictionary.
Abstract class for configuration Cfg_* classes.
uint16_t getFamily() const
Returns address family.
Definition: cfgmgr.h:280
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
Embodies a single client class definition.
T get(T hint) const
Returns value with a hint.
Definition: triplet.h:99
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
std::string getTest() const
Fetches the class&#39;s original match expression.
bool dependOnClass(const TokenPtr &token, const std::string &name)
Checks dependency on a token.
Definition: dependency.cc:15
Represents option definitions used by the DHCP server.
void contextToElement(data::ElementPtr map) const
Merge unparse a user_context object.
Definition: user_context.cc:15
void createOptions(const CfgOptionDefPtr &cfg_option_def)
Iterates over the classes in the dictionary and recreates the options.
std::list< std::string > builtinNames
List of classes for which test expressions cannot be defined.
std::list< std::string > builtinPrefixes
List of built-in client class prefixes i.e.
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
std::vector< ClientClassDefPtr > ClientClassDefList
Defines a list of ClientClassDefPtr&#39;s, using insert order.
Defines the logger used by the top-level component of kea-lfc.
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition: token.h:28
virtual ~ClientClassDef()
Destructor.
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
const Name & name_
Definition: dns/message.cc:693
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:241
void removeClass(const std::string &name)
Removes a given class definition from the dictionary.
const ExpressionPtr & getMatchExpr() const
Fetches the class&#39;s match expression.
bool getRequired() const
Fetches the only if required flag.
void setName(const std::string &name)
Sets the class&#39;s name.
void setCfgOption(const CfgOptionPtr &cfg_option)
Sets the class&#39;s option collection.
ClientClassDef(const std::string &name, const ExpressionPtr &match_expr, const CfgOptionPtr &options=CfgOptionPtr())
Constructor.
bool isClientClassBuiltIn(const ClientClass &client_class)
Check if a client class name is builtin.
void initMatchExpr(uint16_t family)
Iterates over the classes in the dictionary and ensures that that match expressions are initialized...
std::string ClientClass
Defines a single class name.
Definition: classify.h:42
friend std::ostream & operator<<(std::ostream &os, const ClientClassDef &x)
Provides a convenient text representation of the class.
Defines classes for storing client class definitions.
bool getDependOnKnown() const
Fetches the depend on known flag aka use host flag.
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
bool equals(const ClientClassDictionary &other) const
Compares two ClientClassDictionary objects for equality.
void setCfgOptionDef(const CfgOptionDefPtr &cfg_option_def)
Sets the class&#39;s option definition collection.