// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef CLIENT_CLASS_DEF_H #define CLIENT_CLASS_DEF_H #include #include #include #include #include #include #include #include #include #include #include #include #include /// @file client_class_def.h /// /// @brief Defines classes for storing client class definitions /// /// The file defines the class, ClientClassDef, which houses the /// information for single client class such as the class name, the /// logical expression used to identify members of the class, and options /// that may be attributed to class members. /// /// In addition it defines a container class, ClientClassDictionary, which /// is houses class definitions keyed by class name. /// namespace isc { namespace dhcp { /// @brief Error that occurs when an attempt is made to add a duplicate class /// to a class dictionary. class DuplicateClientClassDef : public isc::Exception { public: DuplicateClientClassDef(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) {} }; /// @brief Embodies a single client class definition class ClientClassDef : public data::UserContext, public data::CfgToElement, public data::StampedElement { public: /// @brief Constructor /// /// @param name Name to assign to this class /// @param match_expr Expression the class will use to determine membership /// @param options Collection of options members should be given ClientClassDef(const std::string& name, const ExpressionPtr& match_expr, const CfgOptionPtr& options = CfgOptionPtr()); /// Copy constructor ClientClassDef(const ClientClassDef& rhs); /// @brief Destructor virtual ~ClientClassDef(); /// @brief Fetches the class's name std::string getName() const; /// @brief Sets the class's name /// /// @param name the name to assign the class void setName(const std::string& name); /// @brief Fetches the class's match expression const ExpressionPtr& getMatchExpr() const; /// @brief Sets the class's match expression /// /// @param match_expr the expression to assign the class void setMatchExpr(const ExpressionPtr& match_expr); /// @brief Fetches the class's original match expression std::string getTest() const; /// @brief Sets the class's original match expression /// /// @param test the original expression to assign the class void setTest(const std::string& test); /// @brief Fetches the only if required flag bool getRequired() const; /// @brief Sets the only if required flag /// /// @param required the value of the only if required flag void setRequired(bool required); /// @brief Fetches the depend on known flag aka use host flag bool getDependOnKnown() const; /// @brief Sets the depend on known flag aka use host flag /// /// @param depend_on_known the value of the depend on known flag void setDependOnKnown(bool depend_on_known); /// @brief Fetches the class's option definitions const CfgOptionDefPtr& getCfgOptionDef() const; /// @brief Sets the class's option definition collection /// /// @param cfg_option_def the option definitions to assign the class void setCfgOptionDef(const CfgOptionDefPtr& cfg_option_def); /// @brief Fetches the class's option collection const CfgOptionPtr& getCfgOption() const; /// @brief Sets the class's option collection /// /// @param cfg_option the option collection to assign the class void setCfgOption(const CfgOptionPtr& cfg_option); /// @brief Checks direct dependency. /// /// @param name The client class name. /// /// @return true if the definition depends on the class name, false if not. bool dependOnClass(const std::string& name) const; /// @brief Compares two @c ClientClassDef objects for equality. /// /// @param other Other client class definition to compare to. /// /// @return true if objects are equal, false otherwise. bool equals(const ClientClassDef& other) const; /// @brief Equality operator. /// /// @param other Other client class definition to compare to. /// /// @return true if the definitions equal, false otherwise. bool operator==(const ClientClassDef& other) const { return (equals(other)); } /// @brief Inequality operator. /// /// @param other Other client class definition to compare to. /// /// @return true if the definitions are not equal, false otherwise. bool operator!=(const ClientClassDef& other) const { return (!(equals(other))); } /// @brief Provides a convenient text representation of the class friend std::ostream& operator<<(std::ostream& os, const ClientClassDef& x); /// @brief returns next-server value /// @return next-server value const asiolink::IOAddress& getNextServer() const { return (next_server_); } /// @brief sets the next-server value /// /// @param addr the value to be set void setNextServer(const asiolink::IOAddress& addr) { next_server_ = addr; } /// @brief sets the server-name value /// /// @param sname the value to be set void setSname(const std::string& sname) { sname_ = sname; } /// @brief returns server-hostname value /// @return the vector that contains server-hostname (may be empty if not defined) const std::string& getSname() const { return (sname_); } /// @brief sets the boot-file-name value /// /// @param filename the value to be set void setFilename(const std::string& filename) { filename_ = filename; } /// @brief returns boot-file-name value /// @return the vector that contains boot-file-name (may be empty if not defined) const std::string& getFilename() const { return (filename_); } /// @brief Return valid-lifetime value /// /// @return a triplet containing the valid lifetime. util::Triplet getValid() const { return (valid_); } /// @brief Sets new valid lifetime /// /// @param valid New valid lifetime in seconds. void setValid(const util::Triplet& valid) { valid_ = valid; } /// @brief Return preferred-lifetime value /// /// @return a triplet containing the preferred lifetime. util::Triplet getPreferred() const { return (preferred_); } /// @brief Sets new preferred lifetime /// /// @param preferred New valid lifetime in seconds. void setPreferred(const util::Triplet& preferred) { preferred_ = preferred; } /// @brief Sets offer lifetime for the class. /// /// @param offer_lft the offer lifetime assigned to the class (may be empty if not defined) void setOfferLft(const util::Optional& offer_lft) { offer_lft_ = offer_lft; } /// @brief Returns offer lifetime for the class. /// /// @return offer lifetime value util::Optional getOfferLft() const { return (offer_lft_); } /// @brief Test method which checks if the packet belongs to the class /// /// If the packet belongs to the class, the class is added to the packet. /// /// @param pkt The packet checked if it belongs to the class. virtual void test(PktPtr pkt, const ExpressionPtr& expr_ptr); /// @brief Unparse a configuration object /// /// @return a pointer to unparsed configuration virtual isc::data::ElementPtr toElement() const; private: /// @brief Unique text identifier by which this class is known. std::string name_; /// @brief The logical expression which determines membership in /// this class. ExpressionPtr match_expr_; /// @brief The original expression which determines membership in /// this class. std::string test_; /// @brief The only-if-required flag: when false (the default) membership /// is determined during classification so is available for instance for /// subnet selection. When true, membership is evaluated only when required /// and is usable only for option configuration. bool required_; /// @brief The depend on known aka use host flag: when false (the default), /// the required flag is false and the class has a match expression /// the expression is evaluated in the first pass. When true and the /// two other conditions stand the expression is evaluated later when /// the host reservation membership was determined. /// This flag is set to true during the match expression parsing if /// direct or indirect dependency on the builtin [UN]KNOWN classes is /// detected. bool depend_on_known_; /// @brief The option definition configuration for this class CfgOptionDefPtr cfg_option_def_; /// @brief The option data configuration for this class CfgOptionPtr cfg_option_; /// @brief Next server field /// If set by the next-server parameter, this value will be set /// in the siaddr field of the DHCPv4 packet. asiolink::IOAddress next_server_; /// @brief server-hostname /// If set by the server-hostname parameter, this value will be /// set in the sname field of the DHCPv4 packet. /// This can be up to 64 octets long. std::string sname_; /// @brief boot-file-name /// If set by the boot-file-name parameter, this value will be /// set in the file field of the DHCPv4 packet. /// This can be up to 128 octets long. std::string filename_; /// @brief a Triplet (min/default/max) holding allowed valid lifetime values util::Triplet valid_; /// @brief a Triplet (min/default/max) holding allowed preferred lifetime values util::Triplet preferred_; /// @brief offer lifetime for this class (V4 only). util::Optional offer_lft_; }; class TemplateClientClassDef : public ClientClassDef { public: /// @brief Constructor /// /// @param name Name to assign to this class /// @param match_expr Expression the class will use to determine membership /// @param options Collection of options members should be given TemplateClientClassDef(const std::string& name, const ExpressionPtr& match_expr, const CfgOptionPtr& options = CfgOptionPtr()); /// @brief Test method which checks if the packet belongs to the class /// /// If the packet belongs to the class, the class is added to the packet. /// /// @param pkt The packet checked if it belongs to the class. virtual void test(PktPtr pkt, const ExpressionPtr& expr_ptr) override; /// @brief Unparse a configuration object /// /// @return a pointer to unparsed configuration virtual isc::data::ElementPtr toElement() const override; /// @brief This is a prefix added to the spawned class name /// /// If incoming packet is associated with the template class, the name of /// generated spawned class is prepended with this prefix. /// For example, a packet that associates with the template class "FOO" by /// evaluating the template class expression to BAR will cause the packet to /// be assigned to class SPAWN_FOO_BAR. static const std::string SPAWN_CLASS_PREFIX; }; /// @brief a pointer to an ClientClassDef typedef boost::shared_ptr ClientClassDefPtr; /// @brief Defines a map of ClientClassDef's, keyed by the class name. typedef std::unordered_map ClientClassDefMap; /// @brief Defines a pointer to a ClientClassDefMap typedef boost::shared_ptr ClientClassDefMapPtr; /// @brief Defines a list of ClientClassDefPtr's, using insert order. typedef std::vector ClientClassDefList; /// @brief Defines a pointer to a ClientClassDefList typedef boost::shared_ptr ClientClassDefListPtr; /// @brief Maintains a list of ClientClassDef's class ClientClassDictionary : public isc::data::CfgToElement { public: /// @brief Constructor ClientClassDictionary(); ClientClassDictionary(const ClientClassDictionary& rhs); /// @brief Destructor ~ClientClassDictionary(); /// @brief Adds a new class to the list /// /// @param name Name to assign to this class /// @param match_expr Expression the class will use to determine membership /// @param test Original version of match_expr /// @param required Original value of the only if required flag /// @param depend_on_known Using host so will be evaluated later /// @param options Collection of options members should be given /// @param defs Option definitions (optional) /// @param user_context User context (optional) /// @param next_server next-server value for this class (optional) /// @param sname server-name value for this class (optional) /// @param filename boot-file-name value for this class (optional) /// @param valid valid-lifetime triplet (optional) /// @param preferred preferred-lifetime triplet (optional) /// /// @throw DuplicateClientClassDef if class already exists within the /// dictionary. See @ref dhcp::ClientClassDef::ClientClassDef() for /// others. 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& valid = util::Triplet(), const util::Triplet& preferred = util::Triplet(), bool is_template = false, const util::Optional& offer_lft = util::Optional()); /// @brief Adds a new class to the list /// /// @param class_def pointer to class definition to add /// /// @throw DuplicateClientClassDef if class already exists within the /// dictionary, BadValue if the pointer is empty. void addClass(ClientClassDefPtr& class_def); /// @brief Fetches the class definition for a given class name /// /// @param name the name of the desired class /// /// @return ClientClassDefPtr to the desired class if found, or /// an empty pointer if not. ClientClassDefPtr findClass(const std::string& name) const; /// @brief Removes a given class definition from the dictionary /// /// Removes the class definition from the map if it exists, otherwise /// no harm, no foul. /// /// @param name the name of the class to remove void removeClass(const std::string& name); /// @brief Removes a client class by id. /// /// @param id class id. void removeClass(const uint64_t id); /// @brief Fetches the dictionary's list of classes /// /// @return ClientClassDefListPtr to the list of classes const ClientClassDefListPtr& getClasses() const; /// @brief Checks if the class dictionary is empty. /// /// @return true if there are no classes, false otherwise. bool empty() const; /// @brief Checks direct dependency. /// /// @param name The client class name. /// @param [out] dependent_class Reference to a variable where the /// name of the first class depending on the checked class is set. /// /// @return true if a definition depends on the class name, false if none. bool dependOnClass(const std::string& name, std::string& dependent_class) const; /// @brief Compares two @c ClientClassDictionary objects for equality. /// /// @param other Other client class definition to compare to. /// /// @return true if descriptors equal, false otherwise. bool equals(const ClientClassDictionary& other) const; /// @brief Iterates over the classes in the dictionary and ensures that /// that match expressions are initialized. /// /// @param family Class universe, e.g. AF_INET or AF_INET6. void initMatchExpr(uint16_t family); /// @brief Iterates over the classes in the dictionary and recreates /// the options. /// /// @param cfg_option_def set of option definitions to use. void createOptions(const CfgOptionDefPtr& cfg_option_def); /// @brief Equality operator. /// /// @param other Other client class dictionary to compare to. /// /// @return true if the dictionaries are equal, false otherwise. bool operator==(const ClientClassDictionary& other) const { return (equals(other)); } /// @brief Inequality operator. /// /// @param other Other client class dictionary to compare to. /// /// @return true if the dictionaries are not equal, false otherwise. bool operator!=(const ClientClassDictionary& other) const { return (!equals(other)); } /// @brief Copy assignment operator. /// /// @param rhs Client class dictionary to be copied from. /// @return Instance copy. ClientClassDictionary& operator=(const ClientClassDictionary& rhs); /// @brief Unparse a configuration object /// /// @return a pointer to unparsed configuration virtual isc::data::ElementPtr toElement() const; private: /// @brief Map of the class definitions ClientClassDefMapPtr map_; /// @brief List of the class definitions ClientClassDefListPtr list_; }; /// @brief Defines a pointer to a ClientClassDictionary typedef boost::shared_ptr ClientClassDictionaryPtr; /// @brief List of built-in client class names. /// i.e. ALL, KNOWN, UNKNOWN and BOOTP but not DROP. extern std::list builtinNames; /// @brief List of built-in client class prefixes /// i.e. VENDOR_CLASS_, HA_, AFTER_ and EXTERNAL_. extern std::list builtinPrefixes; /// @brief Check if a client class name is builtin. /// /// @param client_class A client class name to look for. /// @return true if built-in, false if not. bool isClientClassBuiltIn(const ClientClass& client_class); /// @brief Check if a client class name is already defined, /// i.e. is built-in or in the dictionary, /// /// The reference to depend on known flag is set to true if the class /// is KNOWN or UNKNOWN (direct dependency) or has this flag set /// (indirect dependency). /// /// @param class_dictionary A class dictionary where to look for. /// @param depend_on_known A reference to depend on known flag. /// @param client_class A client class name to look for. /// @return true if defined or built-in, false if not. bool isClientClassDefined(ClientClassDictionaryPtr& class_dictionary, bool& depend_on_known, const ClientClass& client_class); } // namespace isc::dhcp } // namespace isc #endif // CLIENT_CLASS_DEF_H