Coverage Report

Created: 2026-07-14 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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