Kea 2.2.0
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
17using namespace isc::data;
18
19namespace isc {
20namespace dhcp {
21
22//********** ClientClassDef ******************//
23
24ClientClassDef::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
70std::string
72 return (name_);
73}
74
75void
76ClientClassDef::setName(const std::string& name) {
77 name_ = name;
78}
79
80const ExpressionPtr&
82 return (match_expr_);
83}
84
85void
87 match_expr_ = match_expr;
88}
89
90std::string
92 return (test_);
93}
94
95void
96ClientClassDef::setTest(const std::string& test) {
97 test_ = test;
98}
99
100bool
102 return (required_);
103}
104
105void
107 required_ = required;
108}
109
110bool
112 return (depend_on_known_);
113}
114
115void
116ClientClassDef::setDependOnKnown(bool depend_on_known) {
117 depend_on_known_ = depend_on_known;
118}
119
120const CfgOptionDefPtr&
122 return (cfg_option_def_);
123}
124
125void
127 cfg_option_def_ = cfg_option_def;
128}
129
130const CfgOptionPtr&
132 return (cfg_option_);
133}
134
135void
137 cfg_option_ = cfg_option;
138}
139
140bool
141ClientClassDef::dependOnClass(const std::string& name) const {
142 return (isc::dhcp::dependOnClass(match_expr_, name));
143}
144
145bool
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();
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
233std::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_)) {
248 addClass(copy);
249 }
250}
251
253}
254
255void
256ClientClassDictionary::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
283void
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
300ClientClassDictionary::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
309void
310ClientClassDictionary::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
321void
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
342bool
344 return (list_->empty());
345}
346
347bool
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
368bool
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
390void
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
411void
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
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_)) {
469 addClass(copy);
470 }
471 }
472 return (*this);
473}
474
476std::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
487std::list<std::string>
489 "VENDOR_CLASS_", "HA_", "AFTER_", "EXTERNAL_"
490};
491
492bool
493isClientClassBuiltIn(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
515bool
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
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
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
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:286
This class represents configuration element which is associated with database identifier,...
uint16_t getFamily() const
Returns address family.
Definition: cfgmgr.h:280
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
Represents option definitions used by the DHCP server.
Represents option data configuration for the DHCP server.
Definition: cfg_option.h:314
Embodies a single client class definition.
bool equals(const ClientClassDef &other) const
Compares two ClientClassDef objects for equality.
bool getRequired() const
Fetches the only if required flag.
void setCfgOption(const CfgOptionPtr &cfg_option)
Sets the class's option collection.
void setName(const std::string &name)
Sets the class's name.
bool dependOnClass(const std::string &name) const
Checks direct dependency.
bool getDependOnKnown() const
Fetches the depend on known flag aka use host flag.
void setRequired(bool required)
Sets the only if required flag.
const CfgOptionDefPtr & getCfgOptionDef() const
Fetches the class's option definitions.
ClientClassDef(const std::string &name, const ExpressionPtr &match_expr, const CfgOptionPtr &options=CfgOptionPtr())
Constructor.
const ExpressionPtr & getMatchExpr() const
Fetches the class's match expression.
void setCfgOptionDef(const CfgOptionDefPtr &cfg_option_def)
Sets the class's option definition collection.
virtual ~ClientClassDef()
Destructor.
std::string getName() const
Fetches the class's name.
const CfgOptionPtr & getCfgOption() const
Fetches the class's option collection.
void setMatchExpr(const ExpressionPtr &match_expr)
Sets the class's match expression.
void setTest(const std::string &test)
Sets the class's original match expression.
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
std::string getTest() const
Fetches the class's original match expression.
void setDependOnKnown(bool depend_on_known)
Sets the depend on known flag aka use host flag.
Maintains a list of ClientClassDef's.
ClientClassDefPtr findClass(const std::string &name) const
Fetches the class definition for a given class name.
bool equals(const ClientClassDictionary &other) const
Compares two ClientClassDictionary objects for equality.
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 removeClass(const std::string &name)
Removes a given class definition from the dictionary.
bool empty() const
Checks if the class dictionary is empty.
bool dependOnClass(const std::string &name, std::string &dependent_class) const
Checks direct dependency.
const ClientClassDefListPtr & getClasses() const
Fetches the dictionary's list of classes.
ClientClassDictionary & operator=(const ClientClassDictionary &rhs)
Copy assignment operator.
void createOptions(const CfgOptionDefPtr &cfg_option_def)
Iterates over the classes in the dictionary and recreates the options.
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
void initMatchExpr(uint16_t family)
Iterates over the classes in the dictionary and ensures that that match expressions are initialized.
Error that occurs when an attempt is made to add a duplicate class to a class dictionary.
Parser for a logical 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.
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:136
T get(T hint) const
Returns value with a hint.
Definition: triplet.h:99
T getMax() const
Returns a maximum allowed value.
Definition: triplet.h:112
T getMin() const
Returns a minimum allowed value.
Definition: triplet.h:85
Defines classes for storing client class definitions.
Parsers for client class definitions.
const Name & name_
Definition: dns/message.cc:693
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
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
std::string ClientClass
Defines a single class name.
Definition: classify.h:42
std::vector< ClientClassDefPtr > ClientClassDefList
Defines a list of ClientClassDefPtr's, using insert order.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:706
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< ClientClassDef > ClientClassDefPtr
a pointer to an ClientClassDef
std::list< std::string > builtinPrefixes
List of built-in client class prefixes i.e.
std::ostream & operator<<(std::ostream &os, const OpaqueDataTuple &tuple)
Inserts the OpaqueDataTuple as a string into stream.
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
boost::shared_ptr< ClientClassDefList > ClientClassDefListPtr
Defines a pointer to a ClientClassDefList.
bool isClientClassDefined(ClientClassDictionaryPtr &class_dictionary, bool &depend_on_known, const ClientClass &client_class)
Check if a client class name is already defined, i.e.
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition: token.h:28
bool isClientClassBuiltIn(const ClientClass &client_class)
Check if a client class name is builtin.
bool dependOnClass(const TokenPtr &token, const std::string &name)
Checks dependency on a token.
Definition: dependency.cc:15
std::list< std::string > builtinNames
List of classes for which test expressions cannot be defined.
std::unordered_map< std::string, ClientClassDefPtr > ClientClassDefMap
Defines a map of ClientClassDef's, keyed by the class name.
Defines the logger used by the top-level component of kea-lfc.
Abstract class for configuration Cfg_* classes.
Base class for user context.
Definition: user_context.h:22
void contextToElement(data::ElementPtr map) const
Merge unparse a user_context object.
Definition: user_context.cc:15