// Copyright (C) 2014-2022 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 CFG_IFACE_H #define CFG_IFACE_H #include #include #include #include #include #include #include #include #include namespace isc { namespace dhcp { /// @brief Exception thrown when duplicated interface names specified. class DuplicateIfaceName : public Exception { public: DuplicateIfaceName(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @brief Exception thrown when specified interface name is invalid. class InvalidIfaceName : public Exception { public: InvalidIfaceName(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @brief Exception thrown when specified interface doesn't exist in a system. class NoSuchIface : public Exception { public: NoSuchIface(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @brief Exception thrown when duplicated address specified. class DuplicateAddress : public Exception { public: DuplicateAddress(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @brief Exception thrown when specified unicast address is not assigned /// to the interface specified. class NoSuchAddress : public Exception { public: NoSuchAddress(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @brief Exception thrown when invalid socket type has been specified /// for the given family. class InvalidSocketType : public Exception { public: InvalidSocketType(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @brief Represents selection of interfaces for DHCP server. /// /// This class manages selection of interfaces on which the DHCP server is /// listening to queries. The interfaces are selected in the server /// configuration by their names or by the pairs of interface names and /// addresses, e.g. eth0/2001:db8:1::1 (DHCPv6) or e.g. eth0/192.168.8.1 /// (DHCPv4). /// /// This class also accepts "wildcard" interface name which, if specified, /// instructs the server to listen on all available interfaces. /// /// Once interfaces have been specified the sockets (either IPv4 or IPv6) /// can be opened by calling @c CfgIface::openSockets function. Kea /// offers configuration parameters to control the types of sockets to be /// opened by the DHCPv4 server. In small deployments it is requires that /// the server can handle messages from the directly connected clients /// which don't have an address yet. Unicasting the response to such /// client is possible by the use of raw sockets. In larger deployments /// it is often the case that whole traffic is received via relays, and /// in such case the use of UDP sockets is preferred. The type of the /// sockets to be opened is specified using one of the /// @c CfgIface::useSocketType method variants. The @c CfgIface::SocketType /// enumeration specifies the possible values. /// /// @warning This class makes use of the AF_INET and AF_INET6 family literals, /// but it doesn't verify that the address family value passed as @c uint16_t /// parameter is equal to one of them. It is a callers responsibility to /// guarantee that the address family value is correct. /// /// The interface name is passed as an argument of the @ref CfgIface::use /// function which controls the selection of the interface on which the /// DHCP queries should be received by the server. The interface name /// passed as the argument of this function may appear in one of the following /// formats: /// - interface-name, e.g. eth0 /// - interface-name/address, e.g. eth0/2001:db8:1::1 or eth0/192.168.8.1 /// /// Extraneous spaces surrounding the interface name and/or address /// are accepted. For example: eth0 / 2001:db8:1::1 will be accepted. /// /// When only interface name is specified (without an address) it is allowed /// to use the "wildcard" interface name (*) which indicates that the server /// should open sockets on all interfaces. When IPv6 is in use, the sockets /// will be bound to the link local addresses. Wildcard interface names are /// not allowed when specifying a unicast address. For example: /// */2001:db8:1::1 is not allowed. /// /// The DHCPv6 configuration accepts simultaneous use of the "interface-name" /// and "interface-name/address" tuple for the same interface, e.g. /// "eth0", "eth0/2001:db8:1::1" specifies that the server should open a /// socket and bind to link local address as well as open a socket bound to /// the specified unicast address. /// /// The DHCPv4 configuration doesn't accept the simultaneous use of the /// "interface-name" and the "interface-name/address" tuple for the /// given interface. When the "interface-name" is specified it implies /// that the sockets will be opened on for all addresses configured on /// this interface. If the tuple of "interface-name/address" is specified /// there will be only one socket opened and bound to the specified address. /// This socket will be configured to listen to the broadcast messages /// reaching the interface as well as unicast messages sent to the address /// to which it is bound. It is allowed to select multiple addresses on the /// particular interface explicitly, e.g. "eth0/192.168.8.1", /// "eth0/192.168.8.2". class CfgIface : public isc::data::UserContext, public isc::data::CfgToElement { public: /// @brief Socket type used by the DHCPv4 server. enum SocketType { /// Raw socket, used for direct DHCPv4 traffic. SOCKET_RAW, /// Datagram socket, i.e. IP/UDP socket. SOCKET_UDP }; /// @brief Indicates how outbound interface is selected for relayed traffic. enum OutboundIface { /// Server sends responses over the same interface on which queries are /// received. SAME_AS_INBOUND, /// Server uses routing to determine the right interface to send response. USE_ROUTING }; /// @brief Keyword used to enable all interfaces. /// /// This keyword can be used instead of the interface name to specify /// that DHCP server should listen on all interfaces. static const char* ALL_IFACES_KEYWORD; /// @brief Constructor. CfgIface(); /// @brief Convenience function which closes all open sockets. /// It stops the receiver thread too. void closeSockets() const; /// @brief Compares two @c CfgIface objects for equality. /// /// @param other An object to be compared with this object. /// /// @return true if objects are equal, false otherwise. bool equals(const CfgIface& other) const; /// @brief Tries to open sockets on selected interfaces. /// /// This function opens sockets bound to link-local address as well as /// sockets bound to unicast address. See @c CfgIface::use function /// documentation for details how to specify interfaces and unicast /// addresses to bind the sockets to. /// This function starts the family receiver. /// /// @param family Address family (AF_INET or AF_INET6). /// @param port Port number to be used to bind sockets to. /// @param use_bcast A boolean flag which indicates if the broadcast /// traffic should be received through the socket. This parameter is /// ignored for IPv6. void openSockets(const uint16_t family, const uint16_t port, const bool use_bcast = true); /// @brief Puts the interface configuration into default state. /// /// This function removes interface names from the set. void reset(); /// @brief Select interface to be used to receive DHCP traffic. /// /// @ref CfgIface for a detail explanation of the interface name argument. /// /// @param family Address family (AF_INET or AF_INET6). /// @param iface_name Explicit interface name, a wildcard name (*) of /// the interface(s) or the pair of interface/unicast-address to be used /// to receive DHCP traffic. /// /// @throw InvalidIfaceName If the interface name is incorrect, e.g. empty. /// @throw NoSuchIface If the specified interface is not present. /// @throw NoSuchAddress If the specified unicast address is not assigned /// to the interface. /// @throw DuplicateIfaceName If the interface is already selected, i.e. /// @throw IOError when specified unicast address is invalid. /// @c CfgIface::use has been already called for this interface. void use(const uint16_t family, const std::string& iface_name); /// @brief Sets the specified socket type to be used by the server. /// /// Supported socket types for DHCPv4 are: /// - @c SOCKET_RAW /// - @c SOCKET_UDP /// /// @param family Address family (AF_INET or AF_INET6). /// @param socket_type Socket type. /// /// @throw InvalidSocketType if the unsupported socket type has been /// specified for the address family. Currently, the socket type /// can only be selected for the AF_INET family. void useSocketType(const uint16_t family, const SocketType& socket_type); /// @brief Sets the specified socket type specified in textual format. /// /// The following names of the socket types are currently supported, and /// can be passed in the @c socket_type parameter: /// - raw - for raw sockets, /// - udp - for the IP/UDP datagram sockets, /// /// @param family Address family (AF_INET or AF_INET6) /// @param socket_type_name Socket type in the textual format. /// /// @throw InvalidSocketType if the unsupported socket type has been /// specified for the address family. Currently, the socket type /// can only be selected for the AF_INET family. void useSocketType(const uint16_t family, const std::string& socket_type_name); /// @brief Returns DHCP socket type used by the server. SocketType getSocketType() const { return (socket_type_); } /// @brief Returns the socket type in the textual format. std::string socketTypeToText() const; /// @brief Sets outbound interface selection mode. /// /// @param outbound_iface New outbound interface selection mode setting. void setOutboundIface(const OutboundIface& outbound_iface); /// @brief Returns outbound interface selection mode. /// /// @return Outbound interface selection mode. OutboundIface getOutboundIface() const; /// @brief Returns outbound interface selection mode as string. /// /// @return text representation of the outbound interface selection mode. std::string outboundTypeToText() const; /// @brief Converts text to outbound interface selection mode. /// /// @param txt either 'same-as-inbound' or 'use-routing' /// @return Outbound interface selection mode. static OutboundIface textToOutboundIface(const std::string& txt); /// @brief Converts the socket type in the textual format to the type /// represented by the @c SocketType. /// /// @throw InvalidSocketType if the specified value of the @c socket_type_name /// is invalid. SocketType textToSocketType(const std::string& socket_type_name) const; /// @brief Equality operator. /// /// @param other Object to be compared with this object. /// /// @return true if objects are equal, false otherwise. bool operator==(const CfgIface& other) const { return (equals(other)); } /// @brief Inequality operator. /// /// @param other Object to be compared with this object. /// /// @return true if objects are not equal, false otherwise. bool operator!=(const CfgIface& other) const { return (!equals(other)); } /// @brief Unparse a configuration object /// /// @return a pointer to unparsed configuration virtual isc::data::ElementPtr toElement() const; /// @brief Set the re-detect flag /// /// @param re_detect the new value of the flag void setReDetect(bool re_detect) { re_detect_ = re_detect; } /// @brief Set flag that Kea must successfully bind all socket services on init. /// /// @param require_all true if all sockets must be bound, false otherwise. void setServiceSocketsRequireAll(bool require_all) { service_socket_require_all_ = require_all; } /// @brief Indicates that Kea must successfully bind all socket services on init. /// /// @return true if all sockets must be bound, false otherwise. bool getServiceSocketsRequireAll() const { return (service_socket_require_all_); } /// @brief Set the socket service binding retry interval between attempts. /// /// @param interval Milliseconds between attempts. void setServiceSocketsRetryWaitTime(uint32_t interval) { service_sockets_retry_wait_time_ = interval; } /// @brief Indicates the socket service binding retry interval between attempts. /// /// @return Milliseconds between attempts. uint32_t getServiceSocketsRetryWaitTime() const { return (service_sockets_retry_wait_time_); } /// @brief Set a maximum number of service sockets bind attempts. /// /// @param max_retries Number of attempts. The value 0 disables retries. void setServiceSocketsMaxRetries(uint32_t max_retries) { service_sockets_max_retries_ = max_retries; } /// @brief Indicates the maximum number of service sockets bind attempts. /// /// @return Number of attempts. uint32_t getServiceSocketsMaxRetries() const { return (service_sockets_max_retries_); } /// @brief Get the reconnect controller. /// /// @return the reconnect controller util::ReconnectCtlPtr getReconnectCtl() const { return (reconnect_ctl_); } /// @brief Represents a callback invoked if all retries of the /// opening sockets fail. typedef std::function OpenSocketsFailedCallback; /// @brief Optional callback function to invoke if all retries of the /// opening sockets fail. static OpenSocketsFailedCallback open_sockets_failed_callback_; private: /// @brief Checks if multiple IPv4 addresses has been activated on any /// interface. /// /// This method is useful to check if the current configuration uses /// multiple IPv4 addresses on any interface. This is important when /// using raw sockets to receive messages from the clients because /// each packet may be received multiple times when it is sent from /// a directly connected client. If this is the case, a warning must /// be logged. /// /// @return true if multiple addresses are activated on any interface, /// false otherwise. static bool multipleAddressesPerInterfaceActive(); /// @brief Selects or deselects interfaces. /// /// This function selects all interfaces to receive DHCP traffic or /// deselects all interfaces so as none of them receives a DHCP traffic. /// /// @param family Address family (AF_INET or AF_INET6). /// @param inactive A boolean value which indicates if all interfaces /// (except loopback) should be selected or deselected. /// @param loopback_inactive A boolean value which indicates if loopback /// interface should be selected or deselected. /// should be deselected/inactive (true) or selected/active (false). void setState(const uint16_t family, const bool inactive, const bool loopback_inactive) const; /// @brief Selects or deselects addresses on the interface. /// /// This function selects all address on the interface to receive DHCP /// traffic or deselects all addresses so as none of them receives the /// DHCP traffic. /// /// @param family Address family (AF_INET or AF_INET6). /// @param active A boolean value which indicates if all addresses should /// be active (if true), or inactive (if false). /// @param iface An interface on which addresses are selected/deselected. void setIfaceAddrsState(const uint16_t family, const bool active, Iface& iface) const; /// @brief Error handler for executed when opening a socket fail. /// /// A pointer to this function is passed to the @c IfaceMgr::openSockets4 /// or @c IfaceMgr::openSockets6. These functions call this handler when /// they fail to open a socket. The handler logs an error passed in the /// parameter. /// /// @param errmsg Error message being logged by the function. static void socketOpenErrorHandler(const std::string& errmsg); /// @brief Calls a family-specific function to open sockets. /// /// It is a static function for a safe call from a CfgIface instance or a /// timer handler. /// /// @param family Address family (AF_INET or AF_INET6). /// @param port Port number to be used to bind sockets to. /// @param can_use_bcast A boolean flag which indicates if the broadcast /// traffic should be received through the socket and the raw sockets are /// used. For the UDP sockets, we only handle the relayed (unicast) /// traffic. This parameter is ignored for IPv6. /// @param skip_opened Omits the already opened sockets (doesn't try to /// re-bind). /// @return Pair of boolean flags. The first boolean is true if at least /// one socket is successfully opened, and the second is true if no errors /// occur. static std::pair openSocketsForFamily(const uint16_t family, const uint16_t port, const bool can_use_bcast, const bool skip_opened); /// @brief Creates a ReconnectCtl based on the configuration's /// retry parameters. /// /// @return The reconnect control created using the configuration /// parameters. util::ReconnectCtlPtr makeReconnectCtl() const; /// Calls the @c CfgIface::openSocketsForFamily function and retry it if /// socket opening fails. /// /// @param reconnect_ctl Used to manage socket reconnection. /// @param family Address family (AF_INET or AF_INET6). /// @param port Port number to be used to bind sockets to. /// @param can_use_bcast A boolean flag which indicates if the broadcast /// traffic should be received through the socket and the raw sockets are /// used. For the UDP sockets, we only handle the relayed (unicast) /// traffic. This parameter is ignored for IPv6. /// /// @return True if at least one socket opened successfully. static bool openSocketsWithRetry(util::ReconnectCtlPtr reconnect_ctl, const uint16_t family, const uint16_t port, const bool can_use_bcast); /// @brief Represents a set of interface names. typedef std::set IfaceSet; /// @brief A set of interface names specified by the user. IfaceSet iface_set_; /// @brief A map of interfaces and addresses to which the server /// should bind sockets. typedef std::multimap ExplicitAddressMap; /// @brief A map which holds the pairs of interface names and addresses /// for which the sockets should be opened. ExplicitAddressMap address_map_; /// @brief A boolean value which indicates that the wildcard interface name /// has been specified (*). bool wildcard_used_; /// @brief A type of the sockets used by the DHCP server. SocketType socket_type_; /// @brief A boolean value which reflects current re-detect setting bool re_detect_; /// @brief A boolean value indicates that Kea must successfully bind all socket services on init bool service_socket_require_all_; /// @brief An interval between attempts to retry the socket service binding. uint32_t service_sockets_retry_wait_time_; /// @brief A maximum number of attempts to bind the service sockets. uint32_t service_sockets_max_retries_; /// @brief Indicates how outbound interface is selected for relayed traffic. OutboundIface outbound_iface_; /// @brief Used to manage socket reconnection. util::ReconnectCtlPtr reconnect_ctl_; }; /// @brief A pointer to the @c CfgIface . typedef boost::shared_ptr CfgIfacePtr; /// @brief A pointer to the const @c CfgIface. typedef boost::shared_ptr ConstCfgIfacePtr; } } #endif // CFG_IFACE_H