]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2976] Added recoverStashedAgentOption
authorFrancis Dupont <fdupont@isc.org>
Sat, 30 Mar 2024 14:38:12 +0000 (15:38 +0100)
committerRazvan Becheriu <razvan@isc.org>
Fri, 26 Apr 2024 15:25:06 +0000 (18:25 +0300)
src/bin/dhcp4/dhcp4_messages.cc
src/bin/dhcp4/dhcp4_messages.h
src/bin/dhcp4/dhcp4_messages.mes
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h

index 3ee62864d8350f54dfdef85ccdc47852ff42f6d5..110dfe06ec8631f8e7e1ced90cb152b53604fca3 100644 (file)
@@ -153,6 +153,7 @@ extern const isc::log::MessageID DHCP4_POST_ALLOCATION_NAME_UPDATE_FAIL = "DHCP4
 extern const isc::log::MessageID DHCP4_QUERY_DATA = "DHCP4_QUERY_DATA";
 extern const isc::log::MessageID DHCP4_QUERY_LABEL = "DHCP4_QUERY_LABEL";
 extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_FAIL = "DHCP4_RECLAIM_EXPIRED_LEASES_FAIL";
+extern const isc::log::MessageID DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO = "DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO";
 extern const isc::log::MessageID DHCP4_RELEASE = "DHCP4_RELEASE";
 extern const isc::log::MessageID DHCP4_RELEASE_DELETED = "DHCP4_RELEASE_DELETED";
 extern const isc::log::MessageID DHCP4_RELEASE_EXCEPTION = "DHCP4_RELEASE_EXCEPTION";
@@ -341,6 +342,7 @@ const char* values[] = {
     "DHCP4_QUERY_DATA", "%1, packet details: %2",
     "DHCP4_QUERY_LABEL", "received query: %1",
     "DHCP4_RECLAIM_EXPIRED_LEASES_FAIL", "failed to reclaim expired leases: %1",
+    "DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO", "recovered for query %1 relay agent option from lease %2: %3",
     "DHCP4_RELEASE", "%1: address %2 was released properly.",
     "DHCP4_RELEASE_DELETED", "%1: address %2 was deleted on release.",
     "DHCP4_RELEASE_EXCEPTION", "%1: while trying to release address %2 an exception occurred: %3",
index 7f9ef8acff6b021cc688967623c420863f881231..9faa16296aa6be00ddedf2edd3640b5a64cc38ce 100644 (file)
@@ -154,6 +154,7 @@ extern const isc::log::MessageID DHCP4_POST_ALLOCATION_NAME_UPDATE_FAIL;
 extern const isc::log::MessageID DHCP4_QUERY_DATA;
 extern const isc::log::MessageID DHCP4_QUERY_LABEL;
 extern const isc::log::MessageID DHCP4_RECLAIM_EXPIRED_LEASES_FAIL;
+extern const isc::log::MessageID DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO;
 extern const isc::log::MessageID DHCP4_RELEASE;
 extern const isc::log::MessageID DHCP4_RELEASE_DELETED;
 extern const isc::log::MessageID DHCP4_RELEASE_EXCEPTION;
index ea0d12922b8c6a66d36ee7ef71f95c06966d0973..9638e4724754db838bde55464a00c64c397ce21f 100644 (file)
@@ -895,6 +895,12 @@ the client and the transaction identification information.
 This error message indicates that the reclaim expired leases operation failed
 and provides the cause of failure.
 
+% DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO recovered for query %1 relay agent option from lease %2: %3
+This debug message indicates that agent options were stashed in the lease for
+the client address of the request and were recovered. The first argument
+includes the request information, the second the client address and the last
+argument the content of the dhcp-agent-options option.
+
 % DHCP4_RELEASE %1: address %2 was released properly.
 This informational message indicates that an address was released properly. It
 is a normal operation during client shutdown. The first argument includes
index 6f7d74b1d377fa684e9f45f08dfc74b337a7f9f0..4c83daec670e8205eb98ddec9fdc40554e399ad8 100644 (file)
@@ -50,6 +50,7 @@
 #include <hooks/hooks_log.h>
 #include <hooks/hooks_manager.h>
 #include <stats/stats_mgr.h>
+#include <util/encode/encode.h>
 #include <util/str.h>
 #include <log/logger.h>
 #include <cryptolink/cryptolink.h>
@@ -399,7 +400,7 @@ Dhcpv4Exchange::copyDefaultOptions() {
     // Do not copy recovered stashed RAI.
     ConstElementPtr sao = CfgMgr::instance().getCurrentCfg()->
         getConfiguredGlobal(CfgGlobals::STASH_AGENT_OPTIONS);
-    if (sao && (sao->getType() == data::Element::boolean) &&
+    if (sao && (sao->getType() == Element::boolean) &&
         sao->boolValue() && query_->inClass("STASH_AGENT_OPTIONS")) {
         return;
     }
@@ -1369,6 +1370,13 @@ Dhcpv4Srv::processPacket(Pkt4Ptr query, bool allow_answer_park) {
     // Update statistics accordingly for received packet.
     processStatsReceived(query);
 
+    // Recover stashed RAI from client address lease.
+    try {
+        recoverStashedAgentOption(query);
+    } catch (const std::exception&) {
+        // Ignore exceptions.
+    }
+
     // Assign this packet to one or more classes if needed. We need to do
     // this before calling accept(), because getSubnet4() may need client
     // class information.
@@ -4320,6 +4328,101 @@ Dhcpv4Srv::processInform(Pkt4Ptr& inform, AllocEngine::ClientContext4Ptr& contex
     return (ex.getResponse());
 }
 
+void
+Dhcpv4Srv::recoverStashedAgentOption(const Pkt4Ptr& query) {
+    if (query->getCiaddr().isV4Zero() || !query->getGiaddr().isV4Zero()) {
+        return;
+    }
+    ConstElementPtr sao = CfgMgr::instance().getCurrentCfg()->
+        getConfiguredGlobal(CfgGlobals::STASH_AGENT_OPTIONS);
+    if (!sao || (sao->getType() != Element::boolean) || !sao->boolValue()) {
+        return;
+    }
+    if (query->getType() != DHCPREQUEST) {
+        return;
+    }
+    OptionPtr rai_opt = query->getOption(DHO_DHCP_AGENT_OPTIONS);
+    if (rai_opt && (rai_opt->len() > Option::OPTION4_HDR_LEN)) {
+        return;
+    }
+    // Should not happen but makes sense to check and gives a trivial way
+    // to disable the feature from previous callout points.
+    if (query->inClass("STASH_AGENT_OPTIONS")) {
+        return;
+    }
+    Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(query->getCiaddr());
+    if (!lease || lease->expired()) {
+        return;
+    }
+    ConstElementPtr user_context = lease->getContext();
+    if (!user_context || (user_context->getType() != Element::map)) {
+        return;
+    }
+    ConstElementPtr isc = user_context->get("ISC");
+    if (!isc || (isc->getType() != Element::map)) {
+        return;
+    }
+    ConstElementPtr extended_info = isc->get("relay-agent-info");
+    if (!extended_info || (extended_info->getType() != Element::map)) {
+        return;
+    }
+    ConstElementPtr relay_agent_info = extended_info->get("relay-agent-info");
+    if (!relay_agent_info) {
+        return;
+    }
+    // Compatibility with the old layout.
+    if (relay_agent_info->getType() == Element::map) {
+        relay_agent_info = relay_agent_info->get("sub-options");
+        if (!relay_agent_info) {
+            return;
+        }
+    }
+    if (relay_agent_info->getType() != Element::string) {
+        return;
+    }
+    // Check ownership before going further.
+    ClientIdPtr client_id;
+    OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
+    if (opt_clientid) {
+        client_id.reset(new ClientId(opt_clientid->getData()));
+    }
+    if (!lease->belongsToClient(query->getHWAddr(), client_id)) {
+        return;
+    }
+    // Extract the RAI.
+    string rai_hex = relay_agent_info->stringValue();
+    vector<uint8_t> rai_data;
+    str::decodeFormattedHexString(rai_hex, rai_data);
+    if (rai_data.size() <= Option::OPTION4_HDR_LEN) {
+        // RAI is empty.
+        return;
+    }
+    static OptionDefinitionPtr rai_def;
+    if (!rai_def) {
+        rai_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
+                                        DHO_DHCP_AGENT_OPTIONS);
+    }
+    if (!rai_def) {
+        // Should not happen.
+        return;
+    }
+    OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4, rai_data));
+    if (!rai) {
+        return;
+    }
+    // Remove an existing empty RAI.
+    if (rai_opt) {
+        query->delOption(DHO_DHCP_AGENT_OPTIONS);
+    }
+    query->addOption(rai);
+    query->addClass("STASH_AGENT_OPTIONS");
+    LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA,
+              DHCP4_RECOVERED_STASHED_RELAY_AGENT_INFO)
+      .arg(query->getLabel())
+      .arg(query->getCiaddr())
+      .arg(rai->toText());
+}
+
 bool
 Dhcpv4Srv::accept(const Pkt4Ptr& query) {
     // Check that the message type is accepted by the server. We rely on the
index a8e1d3c013456ba6eba6af77da06491f647f176a..3bb4bb9ddd9b8da92bb28b51aabc3255cb2e44ca 100644 (file)
@@ -99,12 +99,12 @@ public:
 
     /// @brief Returns the pointer to the server's response.
     ///
-    /// The returned pointer is NULL if the query type is DHCPRELEASE or DHCPDECLINE.
+    /// The returned pointer is null if the query type is DHCPRELEASE or DHCPDECLINE.
     Pkt4Ptr getResponse() const {
         return (resp_);
     }
 
-    /// @brief Removes the response message by resetting the pointer to NULL.
+    /// @brief Removes the response message by resetting the pointer to null.
     void deleteResponse() {
         resp_.reset();
     }
@@ -202,7 +202,7 @@ private:
     /// @warning This message is called internally by @c initResponse and
     /// thus it doesn't check if the resp_ value has been initialized. The
     /// calling method is responsible for making sure that @c resp_ is
-    /// not NULL.
+    /// not null.
     void copyDefaultFields();
 
     /// @brief Copies default options from client's to server's message
@@ -213,7 +213,7 @@ private:
     /// @warning This message is called internally by @c initResponse and
     /// thus it doesn't check if the resp_ value has been initialized. The
     /// calling method is responsible for making sure that @c resp_ is
-    /// not NULL.
+    /// not null.
     void copyDefaultOptions();
 
     /// @brief Pointer to the allocation engine used by the server.
@@ -639,7 +639,7 @@ protected:
     /// @param discover DISCOVER message received from client
     /// @param context pointer to the client context
     ///
-    /// @return OFFER message or NULL
+    /// @return OFFER message or null
     Pkt4Ptr processDiscover(Pkt4Ptr& discover, AllocEngine::ClientContext4Ptr& context);
 
     /// @brief Processes incoming REQUEST and returns REPLY response.
@@ -649,7 +649,7 @@ protected:
     /// is valid, not expired, not reserved, not used by other client and
     /// that requesting client is allowed to use it.
     ///
-    /// Returns ACK message, NAK message, or NULL
+    /// Returns ACK message, NAK message, or null
     ///
     /// @param request a message received from client
     /// @param context pointer to the client context where allocated and
@@ -976,7 +976,7 @@ protected:
     /// @param lease A pointer to the new lease which has been acquired.
     /// @param old_lease A pointer to the instance of the old lease which has
     /// @param ddns_params DDNS configuration parameters
-    /// been replaced by the new lease passed in the first argument. The NULL
+    /// been replaced by the new lease passed in the first argument. The null
     /// value indicates that the new lease has been allocated, rather than
     /// lease being renewed.
     void createNameChangeRequests(const Lease4Ptr& lease,
@@ -1091,7 +1091,7 @@ protected:
     /// @param drop if it is true the packet will be dropped
     /// @param sanity_only if it is true the callout won't be called
     /// @param allow_answer_park Indicates if parking a packet is allowed
-    /// @return selected subnet (or NULL if no suitable subnet was found)
+    /// @return selected subnet (or null if no suitable subnet was found)
     isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& query,
                                        bool& drop,
                                        bool sanity_only = false,
@@ -1108,7 +1108,7 @@ protected:
     /// @param drop if it is true the packet will be dropped
     /// @param sanity_only if it is true the callout won't be called
     /// @param allow_answer_park Indicates if parking a packet is allowed
-    /// @return selected subnet (or NULL if no suitable subnet was found)
+    /// @return selected subnet (or null if no suitable subnet was found)
     isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr& query,
                                           bool& drop,
                                           bool sanity_only = false,
@@ -1138,6 +1138,25 @@ protected:
     /// @param pkt packet to be classified
     void classifyPacket(const Pkt4Ptr& pkt);
 
+    /// @brief Recover stashed agent options from client address lease.
+    ///
+    /// This method checks:
+    ///  - client address is not 0.0.0.0.
+    ///  - relay address is 0.0.0.0.
+    ///  - stash-agent-options is true (vs false, the default).
+    ///  - the query is a DHCPREQUEST.
+    ///  - there is no RAI or an empty RAI in the query.
+    ///  - the query is not member of the STASH_AGENT_OPTIONS client class.
+    ///  - there is a lease for the client address.
+    ///  - the lease is not expired.
+    ///  - the lease has a RAI in its extended info in its user context.
+    ///  - the lease belongs to the client.
+    ///  - a not empty RAI can be recovered from the lease.
+    /// when all checks pass:
+    ///  - add the recovered RAI to the query.
+    ///  - put the query in the STASH_AGENT_OPTIONS client class.
+    void recoverStashedAgentOption(const Pkt4Ptr& query);
+
 protected:
 
     /// @brief Assigns incoming packet to zero or more classes (required pass).