/src/kea/src/lib/dhcpsrv/client_class_def.h
Line | Count | Source |
1 | | // Copyright (C) 2015-2026 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 | | #ifndef CLIENT_CLASS_DEF_H |
8 | | #define CLIENT_CLASS_DEF_H |
9 | | |
10 | | #include <cc/cfg_to_element.h> |
11 | | #include <cc/stamped_element.h> |
12 | | #include <cc/user_context.h> |
13 | | #include <dhcpsrv/cfg_option.h> |
14 | | #include <dhcpsrv/cfg_option_def.h> |
15 | | #include <eval/token.h> |
16 | | #include <exceptions/exceptions.h> |
17 | | #include <util/triplet.h> |
18 | | #include <util/optional.h> |
19 | | |
20 | | #include <string> |
21 | | #include <unordered_map> |
22 | | #include <list> |
23 | | #include <vector> |
24 | | |
25 | | /// @file client_class_def.h |
26 | | /// |
27 | | /// @brief Defines classes for storing client class definitions |
28 | | /// |
29 | | /// The file defines the class, ClientClassDef, which houses the |
30 | | /// information for single client class such as the class name, the |
31 | | /// logical expression used to identify members of the class, and options |
32 | | /// that may be attributed to class members. |
33 | | /// |
34 | | /// In addition it defines a container class, ClientClassDictionary, which |
35 | | /// is houses class definitions keyed by class name. |
36 | | /// |
37 | | namespace isc { |
38 | | namespace dhcp { |
39 | | |
40 | | /// @brief Error that occurs when an attempt is made to add a duplicate class |
41 | | /// to a class dictionary. |
42 | | class DuplicateClientClassDef : public isc::Exception { |
43 | | public: |
44 | | DuplicateClientClassDef(const char* file, size_t line, const char* what) |
45 | 505 | : isc::Exception(file, line, what) {} |
46 | | }; |
47 | | |
48 | | /// @brief Embodies a single client class definition |
49 | | class ClientClassDef : public data::UserContext, |
50 | | public data::CfgToElement, |
51 | | public data::StampedElement { |
52 | | public: |
53 | | /// @brief Constructor |
54 | | /// |
55 | | /// @param name Name to assign to this class |
56 | | /// @param match_expr Expression the class will use to determine membership |
57 | | /// @param options Collection of options members should be given |
58 | | ClientClassDef(const std::string& name, const ExpressionPtr& match_expr, |
59 | | const CfgOptionPtr& options = CfgOptionPtr()); |
60 | | |
61 | | /// Copy constructor |
62 | | ClientClassDef(const ClientClassDef& rhs); |
63 | | |
64 | | /// @brief Destructor |
65 | | virtual ~ClientClassDef(); |
66 | | |
67 | | /// @brief Fetches the class's name |
68 | | std::string getName() const; |
69 | | |
70 | | /// @brief Sets the class's name |
71 | | /// |
72 | | /// @param name the name to assign the class |
73 | | void setName(const std::string& name); |
74 | | |
75 | | /// @brief Fetches the class's match expression |
76 | | const ExpressionPtr& getMatchExpr() const; |
77 | | |
78 | | /// @brief Sets the class's match expression |
79 | | /// |
80 | | /// @param match_expr the expression to assign the class |
81 | | void setMatchExpr(const ExpressionPtr& match_expr); |
82 | | |
83 | | /// @brief Fetches the class's original match expression |
84 | | std::string getTest() const; |
85 | | |
86 | | /// @brief Sets the class's original match expression |
87 | | /// |
88 | | /// @param test the original expression to assign the class |
89 | | void setTest(const std::string& test); |
90 | | |
91 | | /// @brief Fetches the only if additional flag |
92 | | bool getAdditional() const; |
93 | | |
94 | | /// @brief Sets the only if additional flag |
95 | | /// |
96 | | /// @param additional the value of the only if additional flag |
97 | | void setAdditional(bool additional); |
98 | | |
99 | | /// @brief Fetches the depend on known flag aka use host flag |
100 | | bool getDependOnKnown() const; |
101 | | |
102 | | /// @brief Sets the depend on known flag aka use host flag |
103 | | /// |
104 | | /// @param depend_on_known the value of the depend on known flag |
105 | | void setDependOnKnown(bool depend_on_known); |
106 | | |
107 | | /// @brief Fetches the class's option definitions |
108 | | const CfgOptionDefPtr& getCfgOptionDef() const; |
109 | | |
110 | | /// @brief Sets the class's option definition collection |
111 | | /// |
112 | | /// @param cfg_option_def the option definitions to assign the class |
113 | | void setCfgOptionDef(const CfgOptionDefPtr& cfg_option_def); |
114 | | |
115 | | /// @brief Fetches the class's option collection |
116 | | const CfgOptionPtr& getCfgOption() const; |
117 | | |
118 | | /// @brief Sets the class's option collection |
119 | | /// |
120 | | /// @param cfg_option the option collection to assign the class |
121 | | void setCfgOption(const CfgOptionPtr& cfg_option); |
122 | | |
123 | | /// @brief Checks direct dependency. |
124 | | /// |
125 | | /// @param name The client class name. |
126 | | /// |
127 | | /// @return true if the definition depends on the class name, false if not. |
128 | | bool dependOnClass(const std::string& name) const; |
129 | | |
130 | | /// @brief Compares two @c ClientClassDef objects for equality. |
131 | | /// |
132 | | /// @param other Other client class definition to compare to. |
133 | | /// |
134 | | /// @return true if objects are equal, false otherwise. |
135 | | bool equals(const ClientClassDef& other) const; |
136 | | |
137 | | /// @brief Equality operator. |
138 | | /// |
139 | | /// @param other Other client class definition to compare to. |
140 | | /// |
141 | | /// @return true if the definitions equal, false otherwise. |
142 | 0 | bool operator==(const ClientClassDef& other) const { |
143 | 0 | return (equals(other)); |
144 | 0 | } |
145 | | |
146 | | /// @brief Inequality operator. |
147 | | /// |
148 | | /// @param other Other client class definition to compare to. |
149 | | /// |
150 | | /// @return true if the definitions are not equal, false otherwise. |
151 | 0 | bool operator!=(const ClientClassDef& other) const { |
152 | 0 | return (!(equals(other))); |
153 | 0 | } |
154 | | |
155 | | /// @brief Provides a convenient text representation of the class |
156 | | friend std::ostream& operator<<(std::ostream& os, const ClientClassDef& x); |
157 | | |
158 | | /// @brief returns next-server value |
159 | | /// @return next-server value |
160 | 0 | const asiolink::IOAddress& getNextServer() const { |
161 | 0 | return (next_server_); |
162 | 0 | } |
163 | | |
164 | | /// @brief sets the next-server value |
165 | | /// |
166 | | /// @param addr the value to be set |
167 | 16.0k | void setNextServer(const asiolink::IOAddress& addr) { |
168 | 16.0k | next_server_ = addr; |
169 | 16.0k | } |
170 | | |
171 | | /// @brief sets the server-name value |
172 | | /// |
173 | | /// @param sname the value to be set |
174 | 16.0k | void setSname(const std::string& sname) { |
175 | 16.0k | sname_ = sname; |
176 | 16.0k | } |
177 | | |
178 | | /// @brief returns server-hostname value |
179 | | /// @return the vector that contains server-hostname (may be empty if not defined) |
180 | 0 | const std::string& getSname() const { |
181 | 0 | return (sname_); |
182 | 0 | } |
183 | | |
184 | | /// @brief sets the boot-file-name value |
185 | | /// |
186 | | /// @param filename the value to be set |
187 | 16.0k | void setFilename(const std::string& filename) { |
188 | 16.0k | filename_ = filename; |
189 | 16.0k | } |
190 | | |
191 | | /// @brief returns boot-file-name value |
192 | | /// @return the vector that contains boot-file-name (may be empty if not defined) |
193 | 0 | const std::string& getFilename() const { |
194 | 0 | return (filename_); |
195 | 0 | } |
196 | | |
197 | | /// @brief Return valid-lifetime value |
198 | | /// |
199 | | /// @return a triplet containing the valid lifetime. |
200 | 0 | util::Triplet<uint32_t> getValid() const { |
201 | 0 | return (valid_); |
202 | 0 | } |
203 | | |
204 | | /// @brief Sets new valid lifetime |
205 | | /// |
206 | | /// @param valid New valid lifetime in seconds. |
207 | 16.0k | void setValid(const util::Triplet<uint32_t>& valid) { |
208 | 16.0k | valid_ = valid; |
209 | 16.0k | } |
210 | | |
211 | | /// @brief Return preferred-lifetime value |
212 | | /// |
213 | | /// @return a triplet containing the preferred lifetime. |
214 | 0 | util::Triplet<uint32_t> getPreferred() const { |
215 | 0 | return (preferred_); |
216 | 0 | } |
217 | | |
218 | | /// @brief Sets new preferred lifetime |
219 | | /// |
220 | | /// @param preferred New valid lifetime in seconds. |
221 | 16.0k | void setPreferred(const util::Triplet<uint32_t>& preferred) { |
222 | 16.0k | preferred_ = preferred; |
223 | 16.0k | } |
224 | | |
225 | | /// @brief Sets offer lifetime for the class. |
226 | | /// |
227 | | /// @param offer_lft the offer lifetime assigned to the class (may be empty if not defined) |
228 | 16.0k | void setOfferLft(const util::Optional<uint32_t>& offer_lft) { |
229 | 16.0k | offer_lft_ = offer_lft; |
230 | 16.0k | } |
231 | | |
232 | | /// @brief Returns offer lifetime for the class. |
233 | | /// |
234 | | /// @return offer lifetime value |
235 | 0 | util::Optional<uint32_t> getOfferLft() const { |
236 | 0 | return (offer_lft_); |
237 | 0 | } |
238 | | |
239 | | /// @brief Test method which checks if the packet belongs to the class |
240 | | /// |
241 | | /// If the packet belongs to the class, the class is added to the packet. |
242 | | /// |
243 | | /// @param pkt The packet checked if it belongs to the class. |
244 | | /// @param expr_ptr Expression the class will use to determine membership |
245 | | virtual void test(PktPtr pkt, const ExpressionPtr& expr_ptr); |
246 | | |
247 | | /// @brief Unparse a configuration object |
248 | | /// |
249 | | /// @return a pointer to unparsed configuration |
250 | | virtual isc::data::ElementPtr toElement() const; |
251 | | |
252 | | private: |
253 | | /// @brief Unique text identifier by which this class is known. |
254 | | std::string name_; |
255 | | |
256 | | /// @brief The logical expression which determines membership in |
257 | | /// this class. |
258 | | ExpressionPtr match_expr_; |
259 | | |
260 | | /// @brief The original expression which determines membership in |
261 | | /// this class. |
262 | | std::string test_; |
263 | | |
264 | | /// @brief The only-in-additional-list flag: when false (the default) membership |
265 | | /// is determined during classification so is available for instance for |
266 | | /// subnet selection. When true, membership is evaluated only when additional |
267 | | /// and is usable only for option configuration. |
268 | | bool additional_; |
269 | | |
270 | | /// @brief The depend on known aka use host flag: when false (the default), |
271 | | /// the additional flag is false and the class has a match expression |
272 | | /// the expression is evaluated in the first pass. When true and the |
273 | | /// two other conditions stand the expression is evaluated later when |
274 | | /// the host reservation membership was determined. |
275 | | /// This flag is set to true during the match expression parsing if |
276 | | /// direct or indirect dependency on the builtin [UN]KNOWN classes is |
277 | | /// detected. |
278 | | bool depend_on_known_; |
279 | | |
280 | | /// @brief The option definition configuration for this class |
281 | | CfgOptionDefPtr cfg_option_def_; |
282 | | |
283 | | /// @brief The option data configuration for this class |
284 | | CfgOptionPtr cfg_option_; |
285 | | |
286 | | /// @brief Next server field |
287 | | /// If set by the next-server parameter, this value will be set |
288 | | /// in the siaddr field of the DHCPv4 packet. |
289 | | asiolink::IOAddress next_server_; |
290 | | |
291 | | /// @brief server-hostname |
292 | | /// If set by the server-hostname parameter, this value will be |
293 | | /// set in the sname field of the DHCPv4 packet. |
294 | | /// This can be up to 64 octets long. |
295 | | std::string sname_; |
296 | | |
297 | | /// @brief boot-file-name |
298 | | /// If set by the boot-file-name parameter, this value will be |
299 | | /// set in the file field of the DHCPv4 packet. |
300 | | /// This can be up to 128 octets long. |
301 | | std::string filename_; |
302 | | |
303 | | /// @brief a Triplet (min/default/max) holding allowed valid lifetime values |
304 | | util::Triplet<uint32_t> valid_; |
305 | | |
306 | | /// @brief a Triplet (min/default/max) holding allowed preferred lifetime values |
307 | | util::Triplet<uint32_t> preferred_; |
308 | | |
309 | | /// @brief offer lifetime for this class (V4 only). |
310 | | util::Optional<uint32_t> offer_lft_; |
311 | | }; |
312 | | |
313 | | class TemplateClientClassDef : public ClientClassDef { |
314 | | public: |
315 | | /// @brief Constructor |
316 | | /// |
317 | | /// @param name Name to assign to this class |
318 | | /// @param match_expr Expression the class will use to determine membership |
319 | | /// @param options Collection of options members should be given |
320 | | TemplateClientClassDef(const std::string& name, const ExpressionPtr& match_expr, |
321 | | const CfgOptionPtr& options = CfgOptionPtr()); |
322 | | |
323 | | /// @brief Test method which checks if the packet belongs to the class |
324 | | /// |
325 | | /// If the packet belongs to the class, the class is added to the packet. |
326 | | /// |
327 | | /// @param pkt The packet checked if it belongs to the class. |
328 | | /// @param expr_ptr Expression the class will use to determine membership |
329 | | virtual void test(PktPtr pkt, const ExpressionPtr& expr_ptr) override; |
330 | | |
331 | | /// @brief Unparse a configuration object |
332 | | /// |
333 | | /// @return a pointer to unparsed configuration |
334 | | virtual isc::data::ElementPtr toElement() const override; |
335 | | |
336 | | /// @brief This is a prefix added to the spawned class name |
337 | | /// |
338 | | /// If incoming packet is associated with the template class, the name of |
339 | | /// generated spawned class is prepended with this prefix. |
340 | | /// For example, a packet that associates with the template class "FOO" by |
341 | | /// evaluating the template class expression to BAR will cause the packet to |
342 | | /// be assigned to class SPAWN_FOO_BAR. |
343 | | static const std::string SPAWN_CLASS_PREFIX; |
344 | | }; |
345 | | |
346 | | /// @brief a pointer to an ClientClassDef |
347 | | typedef boost::shared_ptr<ClientClassDef> ClientClassDefPtr; |
348 | | |
349 | | /// @brief Defines a map of ClientClassDef's, keyed by the class name. |
350 | | typedef std::unordered_map<std::string, ClientClassDefPtr> ClientClassDefMap; |
351 | | |
352 | | /// @brief Defines a pointer to a ClientClassDefMap |
353 | | typedef boost::shared_ptr<ClientClassDefMap> ClientClassDefMapPtr; |
354 | | |
355 | | /// @brief Defines a list of ClientClassDefPtr's, using insert order. |
356 | | typedef std::vector<ClientClassDefPtr> ClientClassDefList; |
357 | | |
358 | | /// @brief Defines a pointer to a ClientClassDefList |
359 | | typedef boost::shared_ptr<ClientClassDefList> ClientClassDefListPtr; |
360 | | |
361 | | /// @brief Maintains a list of ClientClassDef's |
362 | | class ClientClassDictionary : public isc::data::CfgToElement { |
363 | | |
364 | | public: |
365 | | /// @brief Constructor |
366 | | ClientClassDictionary(); |
367 | | |
368 | | ClientClassDictionary(const ClientClassDictionary& rhs); |
369 | | |
370 | | /// @brief Destructor |
371 | | ~ClientClassDictionary(); |
372 | | |
373 | | /// @brief Adds a new class to the list |
374 | | /// |
375 | | /// @param name Name to assign to this class |
376 | | /// @param match_expr Expression the class will use to determine membership |
377 | | /// @param test Original version of match_expr |
378 | | /// @param additional Original value of the only if additional flag |
379 | | /// @param depend_on_known Using host so will be evaluated later |
380 | | /// @param options Collection of options members should be given |
381 | | /// @param defs Option definitions (optional) |
382 | | /// @param user_context User context (optional) |
383 | | /// @param next_server next-server value for this class (optional) |
384 | | /// @param sname server-name value for this class (optional) |
385 | | /// @param filename boot-file-name value for this class (optional) |
386 | | /// @param valid valid-lifetime triplet (optional) |
387 | | /// @param preferred preferred-lifetime triplet (optional) |
388 | | /// @param is_template true if class is a template class. Defaults to false. |
389 | | /// @param offer_lft the offer lifetime assigned to the class (may be empty if not defined) |
390 | | /// |
391 | | /// @throw DuplicateClientClassDef if class already exists within the |
392 | | /// dictionary. See @ref dhcp::ClientClassDef::ClientClassDef() for |
393 | | /// others. |
394 | | void addClass(const std::string& name, const ExpressionPtr& match_expr, |
395 | | const std::string& test, bool additional, bool depend_on_known, |
396 | | const CfgOptionPtr& options, |
397 | | CfgOptionDefPtr defs = CfgOptionDefPtr(), |
398 | | isc::data::ConstElementPtr user_context = isc::data::ConstElementPtr(), |
399 | | asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"), |
400 | | const std::string& sname = std::string(), |
401 | | const std::string& filename = std::string(), |
402 | | const util::Triplet<uint32_t>& valid = util::Triplet<uint32_t>(), |
403 | | const util::Triplet<uint32_t>& preferred = util::Triplet<uint32_t>(), |
404 | | bool is_template = false, |
405 | | const util::Optional<uint32_t>& offer_lft = util::Optional<uint32_t>()); |
406 | | |
407 | | /// @brief Adds a new class to the list |
408 | | /// |
409 | | /// @param class_def pointer to class definition to add |
410 | | /// |
411 | | /// @throw DuplicateClientClassDef if class already exists within the |
412 | | /// dictionary, BadValue if the pointer is empty. |
413 | | void addClass(const ClientClassDefPtr& class_def); |
414 | | |
415 | | /// @brief Fetches the class definition for a given class name |
416 | | /// |
417 | | /// @param name the name of the desired class |
418 | | /// |
419 | | /// @return ClientClassDefPtr to the desired class if found, or |
420 | | /// an empty pointer if not. |
421 | | ClientClassDefPtr findClass(const std::string& name) const; |
422 | | |
423 | | /// @brief Removes a given class definition from the dictionary |
424 | | /// |
425 | | /// Removes the class definition from the map if it exists, otherwise |
426 | | /// no harm, no foul. |
427 | | /// |
428 | | /// @param name the name of the class to remove |
429 | | void removeClass(const std::string& name); |
430 | | |
431 | | /// @brief Removes a client class by id. |
432 | | /// |
433 | | /// @param id class id. |
434 | | void removeClass(const uint64_t id); |
435 | | |
436 | | /// @brief Fetches the dictionary's list of classes |
437 | | /// |
438 | | /// @return ClientClassDefListPtr to the list of classes |
439 | | const ClientClassDefListPtr& getClasses() const; |
440 | | |
441 | | /// @brief Checks if the class dictionary is empty. |
442 | | /// |
443 | | /// @return true if there are no classes, false otherwise. |
444 | | bool empty() const; |
445 | | |
446 | | /// @brief Checks direct dependency. |
447 | | /// |
448 | | /// @param name The client class name. |
449 | | /// @param [out] dependent_class Reference to a variable where the |
450 | | /// name of the first class depending on the checked class is set. |
451 | | /// |
452 | | /// @return true if a definition depends on the class name, false if none. |
453 | | bool dependOnClass(const std::string& name, std::string& dependent_class) const; |
454 | | |
455 | | /// @brief Compares two @c ClientClassDictionary objects for equality. |
456 | | /// |
457 | | /// @param other Other client class definition to compare to. |
458 | | /// |
459 | | /// @return true if descriptors equal, false otherwise. |
460 | | bool equals(const ClientClassDictionary& other) const; |
461 | | |
462 | | /// @brief Iterates over the classes in the dictionary and ensures that |
463 | | /// that match expressions are initialized. |
464 | | /// |
465 | | /// @param family Class universe, e.g. AF_INET or AF_INET6. |
466 | | void initMatchExpr(uint16_t family); |
467 | | |
468 | | /// @brief Iterates over the classes in the dictionary and recreates |
469 | | /// the options. |
470 | | /// |
471 | | /// @param cfg_option_def set of option definitions to use. |
472 | | void createOptions(const CfgOptionDefPtr& cfg_option_def); |
473 | | |
474 | | /// @brief Iterates over the classes in the dictionary and encapsulates |
475 | | /// suboptions. |
476 | | void encapsulateOptions() const; |
477 | | |
478 | | /// @brief Equality operator. |
479 | | /// |
480 | | /// @param other Other client class dictionary to compare to. |
481 | | /// |
482 | | /// @return true if the dictionaries are equal, false otherwise. |
483 | 0 | bool operator==(const ClientClassDictionary& other) const { |
484 | 0 | return (equals(other)); |
485 | 0 | } |
486 | | |
487 | | /// @brief Inequality operator. |
488 | | /// |
489 | | /// @param other Other client class dictionary to compare to. |
490 | | /// |
491 | | /// @return true if the dictionaries are not equal, false otherwise. |
492 | 0 | bool operator!=(const ClientClassDictionary& other) const { |
493 | 0 | return (!equals(other)); |
494 | 0 | } |
495 | | |
496 | | /// @brief Copy assignment operator. |
497 | | /// |
498 | | /// @param rhs Client class dictionary to be copied from. |
499 | | /// @return Instance copy. |
500 | | ClientClassDictionary& operator=(const ClientClassDictionary& rhs); |
501 | | |
502 | | /// @brief Unparse a configuration object |
503 | | /// |
504 | | /// @return a pointer to unparsed configuration |
505 | | virtual isc::data::ElementPtr toElement() const; |
506 | | |
507 | | private: |
508 | | |
509 | | /// @brief Map of the class definitions |
510 | | ClientClassDefMapPtr map_; |
511 | | |
512 | | /// @brief List of the class definitions |
513 | | ClientClassDefListPtr list_; |
514 | | }; |
515 | | |
516 | | /// @brief Defines a pointer to a ClientClassDictionary |
517 | | typedef boost::shared_ptr<ClientClassDictionary> ClientClassDictionaryPtr; |
518 | | |
519 | | /// @brief List of built-in client class names. |
520 | | /// i.e. ALL, KNOWN, UNKNOWN and BOOTP but not DROP nor REJECT. |
521 | | extern std::list<std::string> builtinNames; |
522 | | |
523 | | /// @brief List of built-in client class prefixes |
524 | | /// i.e. VENDOR_CLASS_, HA_, AFTER_ and EXTERNAL_. |
525 | | extern std::list<std::string> builtinPrefixes; |
526 | | |
527 | | /// @brief Check if a client class name is builtin. |
528 | | /// |
529 | | /// @param client_class A client class name to look for. |
530 | | /// @return true if built-in, false if not. |
531 | | bool isClientClassBuiltIn(const ClientClass& client_class); |
532 | | |
533 | | /// @brief Check if a client class name is already defined, |
534 | | /// i.e. is built-in or in the dictionary, |
535 | | /// |
536 | | /// The reference to depend on known flag is set to true if the class |
537 | | /// is KNOWN or UNKNOWN (direct dependency) or has this flag set |
538 | | /// (indirect dependency). |
539 | | /// |
540 | | /// @param class_dictionary A class dictionary where to look for. |
541 | | /// @param depend_on_known A reference to depend on known flag. |
542 | | /// @param client_class A client class name to look for. |
543 | | /// @return true if defined or built-in, false if not. |
544 | | bool isClientClassDefined(ClientClassDictionaryPtr& class_dictionary, |
545 | | bool& depend_on_known, |
546 | | const ClientClass& client_class); |
547 | | |
548 | | } // namespace isc::dhcp |
549 | | } // namespace isc |
550 | | |
551 | | #endif // CLIENT_CLASS_DEF_H |