From: Marcin Siodelski Date: Wed, 3 Apr 2019 12:59:36 +0000 (+0200) Subject: [#552,!295] Added several inheritance modes in the Network. X-Git-Tag: Kea-1.6.0-beta~286 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=745593ab168ac63e4e1b5f9473fb5a26c0ff7063;p=thirdparty%2Fkea.git [#552,!295] Added several inheritance modes in the Network. --- diff --git a/src/lib/dhcpsrv/network.h b/src/lib/dhcpsrv/network.h index ca45da023f..1f351df971 100644 --- a/src/lib/dhcpsrv/network.h +++ b/src/lib/dhcpsrv/network.h @@ -199,6 +199,24 @@ public: HR_ALL } HRMode; + /// @brief Inheritance "mode" used when fetching an optional @c Network + /// parameter. + /// + /// The following modes are currently supported: + /// - NONE: no inheritance is used, the network specific value is returned + /// regardless if it is specified or not. + /// - PARENT_NETWORK: parent network specific value is returned or unspecified + /// if the parent network doesn't exist. + /// - GLOBAL: global specific value is returned. + /// - ALL: inheritance is used on all levels: network specific value takes + /// precedence over parent specific value over the global value. + enum class Inheritance { + NONE, + PARENT_NETWORK, + GLOBAL, + ALL + }; + /// Pointer to the RelayInfo structure typedef boost::shared_ptr RelayInfoPtr; @@ -245,9 +263,12 @@ public: /// @brief Returns name of the local interface for which this network is /// selected. /// + /// @param inheritance inheritance mode to be used. /// @return Interface name as text. - util::Optional getIface() const { - return (getProperty(&Network::getIface, iface_name_, "interface")); + util::Optional + getIface(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network::getIface, iface_name_, + inheritance, "interface")); }; /// @brief Sets information about relay @@ -340,14 +361,20 @@ public: /// @note The returned reference is only valid as long as the object /// returned it is valid. /// + /// @param inheritance inheritance mode to be used. /// @return client class @ref client_class_ - util::Optional getClientClass() const { - return (getProperty(&Network::getClientClass, client_class_)); + util::Optional + getClientClass(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network::getClientClass, client_class_, + inheritance)); } /// @brief Return valid-lifetime for addresses in that prefix - Triplet getValid() const { - return (getProperty(&Network::getValid, valid_, "valid-lifetime")); + /// + /// @param inheritance inheritance mode to be used. + Triplet getValid(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network::getValid, valid_, inheritance, + "valid-lifetime")); } /// @brief Sets new valid lifetime for a network. @@ -358,8 +385,10 @@ public: } /// @brief Returns T1 (renew timer), expressed in seconds - Triplet getT1() const { - return (getProperty(&Network::getT1, t1_, "renew-timer")); + /// + /// @param inheritance inheritance mode to be used. + Triplet getT1(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network::getT1, t1_, inheritance, "renew-timer")); } /// @brief Sets new renew timer for a network. @@ -370,8 +399,10 @@ public: } /// @brief Returns T2 (rebind timer), expressed in seconds - Triplet getT2() const { - return (getProperty(&Network::getT2, t2_, "rebind-timer")); + /// + /// @param inheritance inheritance mode to be used. + Triplet getT2(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network::getT2, t2_, inheritance, "rebind-timer")); } /// @brief Sets new rebind timer for a network. @@ -388,19 +419,22 @@ public: /// not in the dynamic pool). HR may also be completely disabled for /// performance reasons. /// + /// @param inheritance inheritance mode to be used. /// @return Host reservation mode enabled. util::Optional - getHostReservationMode() const { + getHostReservationMode(const Inheritance& inheritance = Inheritance::ALL) const { // Inheritance for host reservations is a little different than for other // parameters. The reservation at the global level is given as a string. // Thus we call getProperty here without a global name to check if the // host reservation mode is specified on network level only. const util::Optional& hr_mode = getProperty(&Network::getHostReservationMode, - host_reservation_mode_); + host_reservation_mode_, + inheritance); // If HR mode is not specified at network level we need this special // case code to handle conversion of the globally configured HR // mode to an enum. - if (hr_mode.unspecified()) { + if (hr_mode.unspecified() && (inheritance != Inheritance::NONE) && + (inheritance != Inheritance::PARENT_NETWORK)) { // Get global reservation mode. util::Optional hr_mode_name; hr_mode_name = getGlobalProperty(hr_mode_name, "reservation-mode"); @@ -455,9 +489,13 @@ public: } /// @brief Returns whether or not T1/T2 calculation is enabled. - util::Optional getCalculateTeeTimes() const { + /// + /// @param inheritance inheritance mode to be used. + util::Optional + getCalculateTeeTimes(const Inheritance& inheritance = Inheritance::ALL) const { return (getProperty(&Network::getCalculateTeeTimes, calculate_tee_times_, + inheritance, "calculate-tee-times")); } @@ -469,10 +507,12 @@ public: } /// @brief Returns percentage to use when calculating the T1 (renew timer). - util::Optional getT1Percent() const { - return (getProperty(&Network::getT1Percent, - t1_percent_, - "t1-percent")); + /// + /// @param inheritance inheritance mode to be used. + util::Optional + getT1Percent(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network::getT1Percent, t1_percent_, + inheritance, "t1-percent")); } /// @brief Sets new precentage for calculating T1 (renew timer). @@ -483,10 +523,12 @@ public: } /// @brief Returns percentage to use when calculating the T2 (rebind timer). - util::Optional getT2Percent() const { - return (getProperty(&Network::getT2Percent, - t2_percent_, - "t2-percent")); + /// + /// @param inheritance inheritance mode to be used. + util::Optional + getT2Percent(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network::getT2Percent, t2_percent_, + inheritance, "t2-percent")); } /// @brief Sets new precentage for calculating T2 (rebind timer). @@ -559,6 +601,7 @@ protected: /// value if the value is unspecified for this instance. /// @param property Value to be returned when it is specified or when /// no explicit value is specified on upper inheritance levels. + /// @param inheritance inheritance mode to be used. /// @param global_name Optional name of the global parameter which value /// should be returned if the given parameter is not specified on network /// level. This value is empty by default, which indicates that the @@ -568,19 +611,42 @@ protected: /// @return Optional value fetched from this instance level, parent /// network level or global level template - ReturnType getProperty(ReturnType(BaseType::*MethodPointer)() const, + ReturnType getProperty(ReturnType(BaseType::*MethodPointer)(const Inheritance&) const, ReturnType property, - const std::string& global_name = "") const { - // If the value is specified on this level, let's simply return it. - // The lower level value always takes precedence. + const Inheritance& inheritance, + const std::string& global_name = "") const { + + // If no inheritance is not to be used, return the value for this + // network regardless if it is specified or not. + if (inheritance == Inheritance::NONE) { + return (property); + + } else if (inheritance == Inheritance::PARENT_NETWORK) { + ReturnType parent_property; + + // Check if this instance has a parent network. + auto parent = boost::dynamic_pointer_cast(parent_network_.lock()); + if (parent) { + parent_property = ((*parent).*MethodPointer)(Network::Inheritance::NONE); + } + return (parent_property); + + // If global value requested, return it. + } else if (inheritance == Inheritance::GLOBAL) { + return (getGlobalProperty(ReturnType(), global_name)); + } + + // We use inheritance and the value is not specified on the network level. + // Hence, we need to get the parent network specific value or global value. if (property.unspecified()) { // Check if this instance has a parent network. auto parent = boost::dynamic_pointer_cast(parent_network_.lock()); + // If the parent network exists, let's fetch the parent specific + // value. if (parent) { - // Run the same method on the parent instance to fetch the - // parent level (typically shared network level) value. - auto parent_property = ((*parent).*MethodPointer)(); - // If the value is found, return it. + // We're using inheritance so ask for the parent specific network + // and return it only if it is specified. + auto parent_property = ((*parent).*MethodPointer)(inheritance); if (!parent_property.unspecified()) { return (parent_property); } @@ -610,23 +676,42 @@ protected: /// should be called on the parent network instance (typically on /// @c SharedNetwork4 or @c SharedNetwork6) to fetch the parent specific /// value if the value is unspecified for this instance. + /// @param inheritance inheritance mode to be used. /// /// @return Option pointer fetched from this instance level or parent /// network level. template OptionPtr - getOptionProperty(OptionPtr(BaseType::*MethodPointer)() const, - OptionPtr property) const { - // If the value is specified on this level, let's simply return it. - // The lower level value always takes precedence. + getOptionProperty(OptionPtr(BaseType::*MethodPointer)(const Inheritance& inheritance) const, + OptionPtr property, + const Inheritance& inheritance) const { + if (inheritance == Network::Inheritance::NONE) { + return (property); + + } else if (inheritance == Network::Inheritance::PARENT_NETWORK) { + OptionPtr parent_property; + // Check if this instance has a parent network. + auto parent = boost::dynamic_pointer_cast(parent_network_.lock()); + // If the parent network exists, let's fetch the parent specific + // value. + if (parent) { + parent_property = ((*parent).*MethodPointer)(Network::Inheritance::NONE); + } + return (parent_property); + + } else if (inheritance == Network::Inheritance::GLOBAL) { + return (OptionPtr()); + } + + // We use inheritance and the value is not specified on the network level. + // Hence, we need to get the parent network specific value. if (!property) { // Check if this instance has a parent network. auto parent = boost::dynamic_pointer_cast(parent_network_.lock()); if (parent) { - // Run the same method on the parent instance to fetch the - // parent level (typically shared network level) value. - auto parent_property = ((*parent).*MethodPointer)(); - // If the value is found, return it. + // We're using inheritance so ask for the parent specific network + // and return it only if it is specified. + OptionPtr parent_property = (((*parent).*MethodPointer)(inheritance)); if (parent_property) { return (parent_property); } @@ -710,10 +795,13 @@ public: /// @brief Returns the flag indicating if the client identifiers should /// be used to identify the client's lease. /// + /// @param inheritance inheritance mode to be used. /// @return true if client identifiers should be used, false otherwise. - util::Optional getMatchClientId() const { + util::Optional + getMatchClientId(const Inheritance& inheritance = Inheritance::ALL) const { return (getProperty(&Network4::getMatchClientId, match_client_id_, + inheritance, "match-client-id")); } @@ -729,11 +817,13 @@ public: /// @brief Returns the flag indicating if requests for unknown IP addresses /// should be rejected with DHCPNAK instead of ignored. /// + /// @param inheritance inheritance mode to be used.w /// @return true if requests for unknown IP addresses should be rejected, /// false otherwise. - util::Optional getAuthoritative() const { + util::Optional + getAuthoritative(const Inheritance& inheritance = Inheritance::ALL) const { return (getProperty(&Network4::getAuthoritative, authoritative_, - "authoritative")); + inheritance, "authoritative")); } /// @brief Sets the flag indicating if requests for unknown IP addresses @@ -755,9 +845,10 @@ public: /// @brief Returns siaddr for this network. /// /// @return siaddr value - util::Optional getSiaddr() const { + util::Optional + getSiaddr(const Inheritance& inheritance = Inheritance::ALL) const { return (getProperty(&Network4::getSiaddr, siaddr_, - "next-server")); + inheritance, "next-server")); } /// @brief Sets server hostname for the network. @@ -767,10 +858,12 @@ public: /// @brief Returns server hostname for this network. /// + /// @param inheritance inheritance mode to be used. /// @return server hostname value - util::Optional getSname() const { + util::Optional + getSname(const Inheritance& inheritance = Inheritance::ALL) const { return (getProperty(&Network4::getSname, sname_, - "server-hostname")); + inheritance, "server-hostname")); } /// @brief Sets boot file name for the network. @@ -780,10 +873,12 @@ public: /// @brief Returns boot file name for this subnet /// + /// @param inheritance inheritance mode to be used. /// @return boot file name value - util::Optional getFilename() const { + util::Optional + getFilename(const Inheritance& inheritance = Inheritance::ALL) const { return (getProperty(&Network4::getFilename, filename_, - "boot-file-name")); + inheritance, "boot-file-name")); } /// @brief Unparses network object. @@ -827,10 +922,12 @@ public: /// @brief Returns preferred lifetime (in seconds) /// + /// @param inheritance inheritance mode to be used. /// @return a triplet with preferred lifetime - Triplet getPreferred() const { + Triplet + getPreferred(const Inheritance& inheritance = Inheritance::ALL) const { return (getProperty(&Network6::getPreferred, preferred_, - "preferred-lifetime")); + inheritance, "preferred-lifetime")); } /// @brief Sets new preferred lifetime for a network. @@ -842,9 +939,11 @@ public: /// @brief Returns interface-id value (if specified) /// + /// @param inheritance inheritance mode to be used. /// @return interface-id option (if defined) - OptionPtr getInterfaceId() const { - return (getOptionProperty(&Network6::getInterfaceId, interface_id_)); + OptionPtr getInterfaceId(const Inheritance& inheritance = Inheritance::ALL) const { + return (getOptionProperty(&Network6::getInterfaceId, interface_id_, + inheritance)); } /// @brief sets interface-id option (if defined) @@ -857,10 +956,13 @@ public: /// @brief Returns boolean value indicating that the Rapid Commit option /// is supported or unsupported for the subnet. /// + /// @param inheritance inheritance mode to be used. /// @return true if the Rapid Commit option is supported, false otherwise. - util::Optional getRapidCommit() const { + util::Optional + getRapidCommit(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&Network6::getRapidCommit, rapid_commit_, - "rapid-commit")); + inheritance, "rapid-commit")); } /// @brief Enables or disables Rapid Commit option support for the subnet. diff --git a/src/lib/dhcpsrv/tests/network_unittest.cc b/src/lib/dhcpsrv/tests/network_unittest.cc index 757066ac24..478b5c5028 100644 --- a/src/lib/dhcpsrv/tests/network_unittest.cc +++ b/src/lib/dhcpsrv/tests/network_unittest.cc @@ -94,7 +94,8 @@ public: /// of the global value should be tested. template - void testNetworkInheritance(ParameterType1(BaseType2::*GetMethodPointer)() const, + void testNetworkInheritance(ParameterType1(BaseType2::*GetMethodPointer) + (const Network::Inheritance&) const, void(BaseType2::*SetMethodPointer)(const ParameterType2&), typename ParameterType1::ValueType network_value, typename ParameterType1::ValueType global_value, @@ -104,12 +105,13 @@ public: // object should be unspecified until we set the value for the // parent network or a global value. boost::shared_ptr net_child(new BaseType1()); - EXPECT_TRUE(((*net_child).*GetMethodPointer)().unspecified()); + EXPECT_TRUE(((*net_child).*GetMethodPointer)(Network::Inheritance::ALL).unspecified()); // Create parent network and set the value. boost::shared_ptr net_parent(new BaseType1()); ((*net_parent).*SetMethodPointer)(network_value); - EXPECT_EQ(network_value, ((*net_parent).*GetMethodPointer)().get()); + EXPECT_EQ(network_value, + ((*net_parent).*GetMethodPointer)(Network::Inheritance::ALL).get()); // Assign callbacks that fetch global values to the networks. net_child->setFetchGlobalsFn(getFetchGlobalsFn()); @@ -118,16 +120,26 @@ public: // Not all parameters have the corresponding global values. if (test_global_value) { // If there is a global value it should now be returned. - EXPECT_FALSE(((*net_child).*GetMethodPointer)().unspecified()); - EXPECT_EQ(global_value, ((*net_child).*GetMethodPointer)().get()); + EXPECT_FALSE(((*net_child).*GetMethodPointer)(Network::Inheritance::ALL).unspecified()); + EXPECT_EQ(global_value, + ((*net_child).*GetMethodPointer)(Network::Inheritance::ALL).get()); + + EXPECT_FALSE(((*net_child).*GetMethodPointer)(Network::Inheritance::GLOBAL).unspecified()); + EXPECT_EQ(global_value, + ((*net_child).*GetMethodPointer)(Network::Inheritance::GLOBAL).get()); + + + EXPECT_TRUE(((*net_child).*GetMethodPointer)(Network::Inheritance::NONE).unspecified()); + EXPECT_TRUE(((*net_child).*GetMethodPointer)(Network::Inheritance::PARENT_NETWORK).unspecified()); } // Associated the network with its parent. ASSERT_NO_THROW(net_child->setParent(net_parent)); // This time the parent specific value should be returned. - EXPECT_FALSE(((*net_child).*GetMethodPointer)().unspecified()); - EXPECT_EQ(network_value, ((*net_child).*GetMethodPointer)().get()); + EXPECT_FALSE(((*net_child).*GetMethodPointer)(Network::Inheritance::ALL).unspecified()); + EXPECT_EQ(network_value, + ((*net_child).*GetMethodPointer)(Network::Inheritance::ALL).get()); } /// @brief Holds the collection of configured globals. @@ -275,7 +287,10 @@ TEST_F(NetworkTest, inheritanceSupport6) { // Interface-id requires special type of test. boost::shared_ptr net_child(new TestNetwork6()); - EXPECT_TRUE(net_child->getIface().unspecified()); + EXPECT_FALSE(net_child->getInterfaceId()); + EXPECT_FALSE(net_child->getInterfaceId(Network::Inheritance::NONE)); + EXPECT_FALSE(net_child->getInterfaceId(Network::Inheritance::PARENT_NETWORK)); + EXPECT_FALSE(net_child->getInterfaceId(Network::Inheritance::GLOBAL)); OptionPtr interface_id(new Option(Option::V6, D6O_INTERFACE_ID, OptionBuffer(10, 0xFF))); @@ -285,8 +300,29 @@ TEST_F(NetworkTest, inheritanceSupport6) { ASSERT_NO_THROW(net_child->setParent(net_parent)); + // The interface-id belongs to the parent. + EXPECT_TRUE(net_child->getInterfaceId()); + EXPECT_FALSE(net_child->getInterfaceId(Network::Inheritance::NONE)); + EXPECT_TRUE(net_child->getInterfaceId(Network::Inheritance::PARENT_NETWORK)); + EXPECT_FALSE(net_child->getInterfaceId(Network::Inheritance::GLOBAL)); + + // Check the values are expected. + EXPECT_EQ(interface_id, net_child->getInterfaceId()); + EXPECT_EQ(interface_id, net_child->getInterfaceId(Network::Inheritance::PARENT_NETWORK)); + + // Assign different interface id to a child. + interface_id.reset(new Option(Option::V6, D6O_INTERFACE_ID, + OptionBuffer(10, 0xFE))); + net_child->setInterfaceId(interface_id); + + // This time, the child specific value can be fetched. EXPECT_TRUE(net_child->getInterfaceId()); + EXPECT_TRUE(net_child->getInterfaceId(Network::Inheritance::NONE)); + EXPECT_TRUE(net_child->getInterfaceId(Network::Inheritance::PARENT_NETWORK)); + EXPECT_FALSE(net_child->getInterfaceId(Network::Inheritance::GLOBAL)); + EXPECT_EQ(interface_id, net_child->getInterfaceId()); + EXPECT_EQ(interface_id, net_child->getInterfaceId(Network::Inheritance::NONE)); } // Test that child network returns unspecified value if neither @@ -300,6 +336,13 @@ TEST_F(NetworkTest, getPropertyNoParentNoChild) { TEST_F(NetworkTest, getPropertyNoParentChild) { NetworkPtr net_child(new Network()); net_child->setIface("child_iface"); + + EXPECT_FALSE(net_child->getIface().unspecified()); + EXPECT_FALSE(net_child->getIface(Network::Inheritance::NONE).unspecified()); + EXPECT_TRUE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified()); + EXPECT_TRUE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified()); + + EXPECT_EQ("child_iface", net_child->getIface(Network::Inheritance::NONE).get()); EXPECT_EQ("child_iface", net_child->getIface().get()); } @@ -316,6 +359,10 @@ TEST_F(NetworkTest, getPropertyParentNoChild) { ASSERT_NO_THROW(net_child->setParent(net_parent)); EXPECT_FALSE(net_child->getIface().unspecified()); + EXPECT_TRUE(net_child->getIface(Network::Inheritance::NONE).unspecified()); + EXPECT_FALSE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified()); + EXPECT_TRUE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified()); + EXPECT_EQ("parent_iface", net_child->getIface().get()); } @@ -333,6 +380,10 @@ TEST_F(NetworkTest, getPropertyParentChild) { ASSERT_NO_THROW(net_child->setParent(net_parent)); EXPECT_FALSE(net_child->getIface().unspecified()); + EXPECT_FALSE(net_child->getIface(Network::Inheritance::NONE).unspecified()); + EXPECT_FALSE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified()); + EXPECT_TRUE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified()); + EXPECT_EQ("child_iface", net_child->getIface().get()); } @@ -346,6 +397,10 @@ TEST_F(NetworkTest, getPropertyGlobalNoParentNoChild) { net_child->setFetchGlobalsFn(getFetchGlobalsFn()); EXPECT_FALSE(net_child->getIface().unspecified()); + EXPECT_TRUE(net_child->getIface(Network::Inheritance::NONE).unspecified()); + EXPECT_TRUE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified()); + EXPECT_FALSE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified()); + EXPECT_EQ("global_iface", net_child->getIface().get()); }