Kea 1.9.11
client_class_def.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2021 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>
12#include <boost/foreach.hpp>
13
14using namespace isc::data;
15
16namespace isc {
17namespace dhcp {
18
19//********** ClientClassDef ******************//
20
21ClientClassDef::ClientClassDef(const std::string& name,
22 const ExpressionPtr& match_expr,
23 const CfgOptionPtr& cfg_option)
25 match_expr_(match_expr), required_(false), depend_on_known_(false),
26 cfg_option_(cfg_option), next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
27 valid_(), preferred_() {
28
29 // Name can't be blank
30 if (name_.empty()) {
31 isc_throw(BadValue, "Client Class name cannot be blank");
32 }
33
34 // We permit an empty expression for now. This will likely be useful
35 // for automatic classes such as vendor class.
36 // For classes without options, make sure we have an empty collection
37 if (!cfg_option_) {
38 cfg_option_.reset(new CfgOption());
39 }
40}
41
43 : UserContext(rhs), CfgToElement(rhs), StampedElement(rhs), name_(rhs.name_),
44 match_expr_(ExpressionPtr()), test_(rhs.test_), required_(rhs.required_),
45 depend_on_known_(rhs.depend_on_known_), cfg_option_(new CfgOption()),
46 next_server_(rhs.next_server_), sname_(rhs.sname_),
47 filename_(rhs.filename_), valid_(rhs.valid_), preferred_(rhs.preferred_) {
48
49 if (rhs.match_expr_) {
50 match_expr_.reset(new Expression());
51 *match_expr_ = *(rhs.match_expr_);
52 }
53
54 if (rhs.cfg_option_def_) {
55 cfg_option_def_.reset(new CfgOptionDef());
56 rhs.cfg_option_def_->copyTo(*cfg_option_def_);
57 }
58
59 if (rhs.cfg_option_) {
60 rhs.cfg_option_->copyTo(*cfg_option_);
61 }
62}
63
65}
66
67std::string
69 return (name_);
70}
71
72void
73ClientClassDef::setName(const std::string& name) {
74 name_ = name;
75}
76
77const ExpressionPtr&
79 return (match_expr_);
80}
81
82void
84 match_expr_ = match_expr;
85}
86
87std::string
89 return (test_);
90}
91
92void
93ClientClassDef::setTest(const std::string& test) {
94 test_ = test;
95}
96
97bool
99 return (required_);
100}
101
102void
104 required_ = required;
105}
106
107bool
109 return (depend_on_known_);
110}
111
112void
113ClientClassDef::setDependOnKnown(bool depend_on_known) {
114 depend_on_known_ = depend_on_known;
115}
116
117const CfgOptionDefPtr&
119 return (cfg_option_def_);
120}
121
122void
124 cfg_option_def_ = cfg_option_def;
125}
126
127const CfgOptionPtr&
129 return (cfg_option_);
130}
131
132void
134 cfg_option_ = cfg_option;
135}
136
137bool
138ClientClassDef::dependOnClass(const std::string& name) const {
139 return (isc::dhcp::dependOnClass(match_expr_, name));
140}
141
142bool
144 return ((name_ == other.name_) &&
145 ((!match_expr_ && !other.match_expr_) ||
146 (match_expr_ && other.match_expr_ &&
147 (*match_expr_ == *(other.match_expr_)))) &&
148 ((!cfg_option_ && !other.cfg_option_) ||
149 (cfg_option_ && other.cfg_option_ &&
150 (*cfg_option_ == *other.cfg_option_))) &&
151 ((!cfg_option_def_ && !other.cfg_option_def_) ||
152 (cfg_option_def_ && other.cfg_option_def_ &&
153 (*cfg_option_def_ == *other.cfg_option_def_))) &&
154 (required_ == other.required_) &&
155 (depend_on_known_ == other.depend_on_known_) &&
156 (next_server_ == other.next_server_) &&
157 (sname_ == other.sname_) &&
158 (filename_ == other.filename_));
159}
160
163 uint16_t family = CfgMgr::instance().getFamily();
164 ElementPtr result = Element::createMap();
165 // Set user-context
166 contextToElement(result);
167 // Set name
168 result->set("name", Element::create(name_));
169 // Set original match expression (empty string won't parse)
170 if (!test_.empty()) {
171 result->set("test", Element::create(test_));
172 }
173 // Set only-if-required
174 if (required_) {
175 result->set("only-if-required", Element::create(required_));
176 }
177 // Set option-def (used only by DHCPv4)
178 if (cfg_option_def_ && (family == AF_INET)) {
179 result->set("option-def", cfg_option_def_->toElement());
180 }
181 // Set option-data
182 result->set("option-data", cfg_option_->toElement());
183
184 if (family == AF_INET) {
185 // V4 only
186 // Set next-server
187 result->set("next-server", Element::create(next_server_.toText()));
188 // Set server-hostname
189 result->set("server-hostname", Element::create(sname_));
190 // Set boot-file-name
191 result->set("boot-file-name", Element::create(filename_));
192 } else {
193 // V6 only
194 // Set preferred-lifetime
195 if (!preferred_.unspecified()) {
196 result->set("preferred-lifetime",
197 Element::create(static_cast<long long>(preferred_.get())));
198 }
199
200 if (preferred_.getMin() < preferred_.get()) {
201 result->set("min-preferred-lifetime",
202 Element::create(static_cast<long long>(preferred_.getMin())));
203 }
204
205 if (preferred_.getMax() > preferred_.get()) {
206 result->set("max-preferred-lifetime",
207 Element::create(static_cast<long long>(preferred_.getMax())));
208 }
209 }
210
211 // Set valid-lifetime
212 if (!valid_.unspecified()) {
213 result->set("valid-lifetime",
214 Element::create(static_cast<long long>(valid_.get())));
215
216 if (valid_.getMin() < valid_.get()) {
217 result->set("min-valid-lifetime",
218 Element::create(static_cast<long long>(valid_.getMin())));
219 }
220
221 if (valid_.getMax() > valid_.get()) {
222 result->set("max-valid-lifetime",
223 Element::create(static_cast<long long>(valid_.getMax())));
224 }
225 }
226
227 return (result);
228}
229
230std::ostream& operator<<(std::ostream& os, const ClientClassDef& x) {
231 os << "ClientClassDef:" << x.getName();
232 return (os);
233}
234
235//********** ClientClassDictionary ******************//
236
238 : map_(new ClientClassDefMap()), list_(new ClientClassDefList()) {
239}
240
242 : map_(new ClientClassDefMap()), list_(new ClientClassDefList()) {
243 BOOST_FOREACH(ClientClassDefPtr cclass, *(rhs.list_)) {
245 addClass(copy);
246 }
247}
248
250}
251
252void
253ClientClassDictionary::addClass(const std::string& name,
254 const ExpressionPtr& match_expr,
255 const std::string& test,
256 bool required,
257 bool depend_on_known,
258 const CfgOptionPtr& cfg_option,
259 CfgOptionDefPtr cfg_option_def,
260 ConstElementPtr user_context,
261 asiolink::IOAddress next_server,
262 const std::string& sname,
263 const std::string& filename,
264 const Triplet<uint32_t>& valid,
265 const Triplet<uint32_t>& preferred) {
266 ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
267 cclass->setTest(test);
268 cclass->setRequired(required);
269 cclass->setDependOnKnown(depend_on_known);
270 cclass->setCfgOptionDef(cfg_option_def);
271 cclass->setContext(user_context),
272 cclass->setNextServer(next_server);
273 cclass->setSname(sname);
274 cclass->setFilename(filename);
275 cclass->setValid(valid);
276 cclass->setPreferred(preferred);
277 addClass(cclass);
278}
279
280void
282 if (!class_def) {
283 isc_throw(BadValue, "ClientClassDictionary::addClass "
284 " - class definition cannot be null");
285 }
286
287 if (findClass(class_def->getName())) {
288 isc_throw(DuplicateClientClassDef, "Client Class: "
289 << class_def->getName() << " has already been defined");
290 }
291
292 list_->push_back(class_def);
293 (*map_)[class_def->getName()] = class_def;
294}
295
297ClientClassDictionary::findClass(const std::string& name) const {
298 ClientClassDefMap::iterator it = map_->find(name);
299 if (it != map_->end()) {
300 return (*it).second;
301 }
302
303 return (ClientClassDefPtr());
304}
305
306void
307ClientClassDictionary::removeClass(const std::string& name) {
308 for (ClientClassDefList::iterator this_class = list_->begin();
309 this_class != list_->end(); ++this_class) {
310 if ((*this_class)->getName() == name) {
311 list_->erase(this_class);
312 break;
313 }
314 }
315 map_->erase(name);
316}
317
318void
320 // Class id equal to 0 means it wasn't set.
321 if (id == 0) {
322 return;
323 }
324 for (ClientClassDefList::iterator this_class = list_->begin();
325 this_class != list_->end(); ++this_class) {
326 if ((*this_class)->getId() == id) {
327 map_->erase((*this_class)->getName());
328 list_->erase(this_class);
329 break;
330 }
331 }
332}
333
336 return (list_);
337}
338
339bool
341 return (list_->empty());
342}
343
344bool
346 std::string& dependent_class) const {
347 // Skip previous classes as they should not depend on name.
348 bool found = false;
349 for (ClientClassDefList::iterator this_class = list_->begin();
350 this_class != list_->end(); ++this_class) {
351 if (found) {
352 if ((*this_class)->dependOnClass(name)) {
353 dependent_class = (*this_class)->getName();
354 return (true);
355 }
356 } else {
357 if ((*this_class)->getName() == name) {
358 found = true;
359 }
360 }
361 }
362 return (false);
363}
364
365bool
367 if (list_->size() != other.list_->size()) {
368 return (false);
369 }
370
371 ClientClassDefList::const_iterator this_class = list_->cbegin();
372 ClientClassDefList::const_iterator other_class = other.list_->cbegin();
373 while (this_class != list_->cend() &&
374 other_class != other.list_->cend()) {
375 if (!*this_class || !*other_class ||
376 **this_class != **other_class) {
377 return false;
378 }
379
380 ++this_class;
381 ++other_class;
382 }
383
384 return (true);
385}
386
389 ElementPtr result = Element::createList();
390 // Iterate on the map
391 for (ClientClassDefList::const_iterator this_class = list_->begin();
392 this_class != list_->cend(); ++this_class) {
393 result->add((*this_class)->toElement());
394 }
395 return (result);
396}
397
400 if (this != &rhs) {
401 list_->clear();
402 map_->clear();
403 for (auto cclass : *(rhs.list_)) {
405 addClass(copy);
406 }
407 }
408 return (*this);
409}
410
411std::list<std::string>
413 // DROP is not in this list because it is special but not built-in.
414 // In fact DROP is set from an expression as callouts can drop
415 // directly the incoming packet. The expression must not depend on
416 // KNOWN/UNKNOWN which are set far after the drop point.
417 "ALL", "KNOWN", "UNKNOWN", "BOOTP"
418};
419
420std::list<std::string>
422 "VENDOR_CLASS_", "HA_", "AFTER_", "EXTERNAL_"
423};
424
425bool
426isClientClassBuiltIn(const ClientClass& client_class) {
427 for (std::list<std::string>::const_iterator bn = builtinNames.cbegin();
428 bn != builtinNames.cend(); ++bn) {
429 if (client_class == *bn) {
430 return true;
431 }
432 }
433
434 for (std::list<std::string>::const_iterator bt = builtinPrefixes.cbegin();
435 bt != builtinPrefixes.cend(); ++bt) {
436 if (client_class.size() <= bt->size()) {
437 continue;
438 }
439 auto mis = std::mismatch(bt->cbegin(), bt->cend(), client_class.cbegin());
440 if (mis.first == bt->cend()) {
441 return true;
442 }
443 }
444
445 return false;
446}
447
448bool
450 bool& depend_on_known,
451 const ClientClass& client_class) {
452 // First check built-in classes
453 if (isClientClassBuiltIn(client_class)) {
454 // Check direct dependency on [UN]KNOWN
455 if ((client_class == "KNOWN") || (client_class == "UNKNOWN")) {
456 depend_on_known = true;
457 }
458 return (true);
459 }
460
461 // Second check already defined, i.e. in the dictionary
462 ClientClassDefPtr def = class_dictionary->findClass(client_class);
463 if (def) {
464 // Check indirect dependency on [UN]KNOWN
465 if (def->getDependOnKnown()) {
466 depend_on_known = true;
467 }
468 return (true);
469 }
470
471 // Not defined...
472 return (false);
473}
474
475} // namespace isc::dhcp
476} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
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 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 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 Triplet< uint32_t > &valid=Triplet< uint32_t >(), const Triplet< uint32_t > &preferred=Triplet< uint32_t >())
Adds a new class to the list.
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
Error that occurs when an attempt is made to add a duplicate class to a class dictionary.
This template specifies a parameter value.
Definition: triplet.h:37
Defines classes for storing 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:1152
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:37
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 built-in client class names.
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