]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2022] Added parking points
authorFrancis Dupont <fdupont@isc.org>
Wed, 21 Feb 2024 21:48:41 +0000 (22:48 +0100)
committerFrancis Dupont <fdupont@isc.org>
Fri, 23 Feb 2024 09:54:45 +0000 (10:54 +0100)
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 5e7f9bf8c1535789b7b31d10222688bd0d5a6336..b31f22c7d211988a6312aaa629e9131a9abe0786 100644 (file)
@@ -95,6 +95,7 @@ extern const isc::log::MessageID DHCP4_HOOK_PACKET_RCVD_SKIP = "DHCP4_HOOK_PACKE
 extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_DROP = "DHCP4_HOOK_PACKET_SEND_DROP";
 extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_SKIP = "DHCP4_HOOK_PACKET_SEND_SKIP";
 extern const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_DROP = "DHCP4_HOOK_SUBNET4_SELECT_DROP";
+extern const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_PARK = "DHCP4_HOOK_SUBNET4_SELECT_PARK";
 extern const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_SKIP = "DHCP4_HOOK_SUBNET4_SELECT_SKIP";
 extern const isc::log::MessageID DHCP4_INFORM_DIRECT_REPLY = "DHCP4_INFORM_DIRECT_REPLY";
 extern const isc::log::MessageID DHCP4_INIT_FAIL = "DHCP4_INIT_FAIL";
@@ -279,6 +280,7 @@ const char* values[] = {
     "DHCP4_HOOK_PACKET_SEND_DROP", "%1: prepared DHCPv4 response was not sent because a callout set the next ste to DROP",
     "DHCP4_HOOK_PACKET_SEND_SKIP", "%1: prepared response is not sent, because a callout set the next stp to SKIP",
     "DHCP4_HOOK_SUBNET4_SELECT_DROP", "%1: packet was dropped, because a callout set the next step to 'drop'",
+    "DHCP4_HOOK_SUBNET4_SELECT_PARK", "%1: packet was parked",
     "DHCP4_HOOK_SUBNET4_SELECT_SKIP", "%1: no subnet was selected, because a callout set the next skip flag",
     "DHCP4_INFORM_DIRECT_REPLY", "%1: DHCPACK in reply to the DHCPINFORM will be sent directly to %2 over %3",
     "DHCP4_INIT_FAIL", "failed to initialize Kea server: %1",
index a5baf1e75f9b5cf20e08db9ecb354f8c3abad180..8427698ed19b8706b049ef2f068023988cd3edc7 100644 (file)
@@ -96,6 +96,7 @@ extern const isc::log::MessageID DHCP4_HOOK_PACKET_RCVD_SKIP;
 extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_DROP;
 extern const isc::log::MessageID DHCP4_HOOK_PACKET_SEND_SKIP;
 extern const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_DROP;
+extern const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_PARK;
 extern const isc::log::MessageID DHCP4_HOOK_SUBNET4_SELECT_SKIP;
 extern const isc::log::MessageID DHCP4_INFORM_DIRECT_REPLY;
 extern const isc::log::MessageID DHCP4_INIT_FAIL;
index 5818788a2f4d58f073b82bf5966edf7b77e6ec62..a439bbdb32ed31d82d32182388c2917ab303fdf5 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2012-2024 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
@@ -523,6 +523,11 @@ point, the setting to that value instructs the server to drop the received
 packet. The argument specifies the client and transaction identification
 information.
 
+% DHCP4_HOOK_SUBNET4_SELECT_PARK %1: packet was parked
+This debug message is printed when a callout installed on the
+subnet4_select hook point set the park flag. The argument holds the
+client and transaction identification information.
+
 % DHCP4_HOOK_SUBNET4_SELECT_SKIP %1: no subnet was selected, because a callout set the next skip flag
 This debug message is printed when a callout installed on the
 subnet4_select hook point sets the next step to SKIP value. For this particular hook
index 30c26bfb6b644bf37db01a28f2dbff73688d41af..5359f44cb46e7661f97f06f24f355af27fd21f97 100644 (file)
@@ -715,11 +715,11 @@ Dhcpv4Srv::shutdown() {
 
 isc::dhcp::Subnet4Ptr
 Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query, bool& drop,
-                        bool sanity_only) const {
+                        bool sanity_only, bool allow_answer_park) {
 
     // DHCPv4-over-DHCPv6 is a special (and complex) case
     if (query->isDhcp4o6()) {
-        return (selectSubnet4o6(query, drop, sanity_only));
+        return (selectSubnet4o6(query, drop, sanity_only, allow_answer_park));
     }
 
     Subnet4Ptr subnet;
@@ -751,9 +751,34 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query, bool& drop,
                                     cfgmgr.getCurrentCfg()->
                                     getCfgSubnets4()->getAll());
 
+        // We proactively park the packet.
+        HooksManager::park("subnet4_select", query,
+                           [this, query, allow_answer_park] () {
+                               processLocalizedQuery4AndSendResponse(query,
+                                                                     allow_answer_park);
+                           });
+
         // Call user (and server-side) callouts
-        HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
-                                   *callout_handle);
+        try {
+            HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
+                                       *callout_handle);
+        } catch (...) {
+            // Make sure we don't orphan a parked packet.
+            HooksManager::drop("subnet4_select", query);
+            throw;
+        }
+
+        // Callouts parked the packet. Same as drop but callouts will resume
+        // processing or drop the packet later.
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
+                      DHCP4_HOOK_SUBNET4_SELECT_PARK)
+                .arg(query->getLabel());
+            drop = true;
+            return (Subnet4Ptr());
+        } else {
+            HooksManager::drop("subnet4_select", query);
+        }
 
         // Callouts decided to skip this step. This means that no subnet
         // will be selected. Packet processing will continue, but it will
@@ -801,7 +826,7 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query, bool& drop,
 
 isc::dhcp::Subnet4Ptr
 Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query, bool& drop,
-                           bool sanity_only) const {
+                           bool sanity_only, bool allow_answer_park) {
     Subnet4Ptr subnet;
 
     SubnetSelector selector;
@@ -861,6 +886,9 @@ Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query, bool& drop,
         // handle and its arguments.
         ScopedCalloutHandleState callout_handle_state(callout_handle);
 
+        // Enable copying options from the packet within hook library.
+        ScopedEnableOptionsCopy<Pkt4> query4_options_copy(query);
+
         // Set new arguments
         callout_handle->setArgument("query4", query);
         callout_handle->setArgument("subnet4", subnet);
@@ -868,9 +896,34 @@ Dhcpv4Srv::selectSubnet4o6(const Pkt4Ptr& query, bool& drop,
                                     cfgmgr.getCurrentCfg()->
                                     getCfgSubnets4()->getAll());
 
+        // We proactively park the packet.
+        HooksManager::park("subnet4_select", query,
+                           [this, query, allow_answer_park] () {
+                               processLocalizedQuery4AndSendResponse(query,
+                                                                     allow_answer_park);
+                           });
+
         // Call user (and server-side) callouts
-        HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
-                                   *callout_handle);
+        try {
+            HooksManager::callCallouts(Hooks.hook_index_subnet4_select_,
+                                       *callout_handle);
+        } catch (...) {
+            // Make sure we don't orphan a parked packet.
+            HooksManager::drop("subnet4_select", query);
+            throw;
+        }
+
+        // Callouts parked the packet. Same as drop but callouts will resume
+        // processing or drop the packet later.
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
+                      DHCP4_HOOK_SUBNET4_SELECT_PARK)
+                .arg(query->getLabel());
+            drop = true;
+            return (Subnet4Ptr());
+        } else {
+            HooksManager::drop("subnet4_select", query);
+        }
 
         // Callouts decided to skip this step. This means that no subnet
         // will be selected. Packet processing will continue, but it will
@@ -1147,7 +1200,7 @@ Dhcpv4Srv::processPacketAndSendResponse(Pkt4Ptr query) {
 }
 
 Pkt4Ptr
-Dhcpv4Srv::processPacket(Pkt4Ptr query, bool allow_packet_park) {
+Dhcpv4Srv::processPacket(Pkt4Ptr query, bool allow_answer_park) {
     query->addPktEvent("process_started");
 
     // All packets belong to ALL.
@@ -1326,14 +1379,14 @@ Dhcpv4Srv::processPacket(Pkt4Ptr query, bool allow_packet_park) {
         return (Pkt4Ptr());
     }
 
-    return (processDhcp4Query(query, allow_packet_park));
+    return (processDhcp4Query(query, allow_answer_park));
 }
 
 void
 Dhcpv4Srv::processDhcp4QueryAndSendResponse(Pkt4Ptr query,
-                                            bool allow_packet_park) {
+                                            bool allow_answer_park) {
     try {
-        Pkt4Ptr rsp = processDhcp4Query(query, allow_packet_park);
+        Pkt4Ptr rsp = processDhcp4Query(query, allow_answer_park);
         if (!rsp) {
             return;
         }
@@ -1349,7 +1402,7 @@ Dhcpv4Srv::processDhcp4QueryAndSendResponse(Pkt4Ptr query,
 }
 
 Pkt4Ptr
-Dhcpv4Srv::processDhcp4Query(Pkt4Ptr query, bool allow_packet_park) {
+Dhcpv4Srv::processDhcp4Query(Pkt4Ptr query, bool allow_answer_park) {
     // Create a client race avoidance RAII handler.
     ClientHandler client_handler;
 
@@ -1361,7 +1414,7 @@ Dhcpv4Srv::processDhcp4Query(Pkt4Ptr query, bool allow_packet_park) {
          (query->getType() == DHCPDECLINE))) {
         ContinuationPtr cont =
             makeContinuation(std::bind(&Dhcpv4Srv::processDhcp4QueryAndSendResponse,
-                                       this, query, allow_packet_park));
+                                       this, query, allow_answer_park));
         if (!client_handler.tryLock(query, cont)) {
             return (Pkt4Ptr());
         }
@@ -1378,7 +1431,7 @@ Dhcpv4Srv::processDhcp4Query(Pkt4Ptr query, bool allow_packet_park) {
             (query->getType() == DHCPREQUEST) ||
             (query->getType() == DHCPINFORM)) {
             bool drop = false;
-            ctx->subnet_ = selectSubnet(query, drop);
+            ctx->subnet_ = selectSubnet(query, drop, false, allow_answer_park);
             // Stop here if selectSubnet decided to drop the packet
             if (drop) {
                 return (Pkt4Ptr());
@@ -1402,14 +1455,15 @@ Dhcpv4Srv::processDhcp4Query(Pkt4Ptr query, bool allow_packet_park) {
                                                   static_cast<int64_t>(1));
     }
 
-    return (processLocalizedQuery4(ctx, allow_packet_park));
+    return (processLocalizedQuery4(ctx, allow_answer_park));
 }
 
 void
 Dhcpv4Srv::processLocalizedQuery4AndSendResponse(Pkt4Ptr query,
-                                                 AllocEngine::ClientContext4Ptr& ctx) {
+                                                 AllocEngine::ClientContext4Ptr& ctx,
+                                                 bool allow_answer_park) {
     try {
-        Pkt4Ptr rsp = processLocalizedQuery4(ctx, true);
+        Pkt4Ptr rsp = processLocalizedQuery4(ctx, allow_answer_park);
         if (!rsp) {
             return;
         }
@@ -1425,9 +1479,27 @@ Dhcpv4Srv::processLocalizedQuery4AndSendResponse(Pkt4Ptr query,
     }
 }
 
+void
+Dhcpv4Srv::processLocalizedQuery4AndSendResponse(Pkt4Ptr query,
+                                                 bool allow_answer_park) {
+    // Initialize context.
+    AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4());
+    initContext0(query, ctx);
+
+    // Subnet is cached in the callout context associated to the query.
+    try {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
+        callout_handle->getContext("subnet4", ctx->subnet_);
+    } catch (const Exception&) {
+        // No subnet, leave it to null...
+    }
+
+    processLocalizedQuery4AndSendResponse(query, ctx, allow_answer_park);
+}
+
 Pkt4Ptr
 Dhcpv4Srv::processLocalizedQuery4(AllocEngine::ClientContext4Ptr& ctx,
-                                  bool allow_packet_park) {
+                                  bool allow_answer_park) {
     if (!ctx) {
         isc_throw(Unexpected, "null context");
     }
@@ -1553,7 +1625,7 @@ Dhcpv4Srv::processLocalizedQuery4(AllocEngine::ClientContext4Ptr& ctx,
                 callout_handle->setArgument("deleted_leases4", deleted_leases);
             }
 
-            if (allow_packet_park) {
+            if (allow_answer_park) {
                 // Get the parking limit. Parsing should ensure the value is present.
                 uint32_t parked_packet_limit = 0;
                 data::ConstElementPtr ppl = CfgMgr::instance().getCurrentCfg()->
@@ -1636,7 +1708,7 @@ Dhcpv4Srv::processLocalizedQuery4(AllocEngine::ClientContext4Ptr& ctx,
                 HooksManager::callCallouts(hook_idx, *callout_handle);
             } catch (...) {
                 // Make sure we don't orphan a parked packet.
-                if (allow_packet_park) {
+                if (allow_answer_park) {
                     HooksManager::drop(hook_label, query);
                 }
 
@@ -1644,7 +1716,7 @@ Dhcpv4Srv::processLocalizedQuery4(AllocEngine::ClientContext4Ptr& ctx,
             }
 
             if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_PARK) &&
-                allow_packet_park) {
+                allow_answer_park) {
                 LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, pkt_park_msg)
                     .arg(query->getLabel());
                 // Since the hook library(ies) are going to do the unparking, then
@@ -4172,7 +4244,7 @@ Dhcpv4Srv::processInform(Pkt4Ptr& inform, AllocEngine::ClientContext4Ptr& contex
 }
 
 bool
-Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
+Dhcpv4Srv::accept(const Pkt4Ptr& query) {
     // Check that the message type is accepted by the server. We rely on the
     // function called to log a message if needed.
     if (!acceptMessageType(query)) {
@@ -4200,7 +4272,7 @@ Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
 }
 
 bool
-Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) const {
+Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) {
     // Accept all relayed messages.
     if (pkt->isRelayed()) {
         return (true);
index a38c1528664aa7f1a3c662fd77161256b2eb42a0..eb530280ec46b5ac93a5ffaea41607af96521ae0 100644 (file)
@@ -369,9 +369,9 @@ public:
     /// methods, generates appropriate answer.
     ///
     /// @param query A pointer to the packet to be processed.
-    /// @param allow_packet_park Indicates if parking a packet is allowed.
+    /// @param allow_answer_park Indicates if parking a packet is allowed.
     /// @return A pointer to the response.
-    Pkt4Ptr processPacket(Pkt4Ptr query, bool allow_packet_park = true);
+    Pkt4Ptr processPacket(Pkt4Ptr query, bool allow_answer_park = true);
 
     /// @brief Process a single incoming DHCPv4 query.
     ///
@@ -379,9 +379,9 @@ public:
     /// generates appropriate answer.
     ///
     /// @param query A pointer to the packet to be processed.
-    /// @param allow_packet_park Indicates if parking a packet is allowed.
+    /// @param allow_answer_park Indicates if parking a packet is allowed.
     /// @return A pointer to the response.
-    Pkt4Ptr processDhcp4Query(Pkt4Ptr query, bool allow_packet_park);
+    Pkt4Ptr processDhcp4Query(Pkt4Ptr query, bool allow_answer_park);
 
     /// @brief Process a single incoming DHCPv4 query.
     ///
@@ -389,19 +389,32 @@ public:
     /// generates appropriate answer, sends the answer to the client.
     ///
     /// @param query A pointer to the packet to be processed.
-    /// @param allow_packet_park Indicates if parking a packet is allowed.
+    /// @param allow_answer_park Indicates if parking a packet is allowed.
     void processDhcp4QueryAndSendResponse(Pkt4Ptr query,
-                                          bool allow_packet_park);
+                                          bool allow_answer_park);
 
     /// @brief Process a localized incoming DHCPv4 query.
     ///
     /// It calls per-type processXXX methods, generates appropriate answer.
     ///
     /// @param ctx Pointer to The client context.
-    /// @param allow_packet_park Indicates if parking a packet is allowed.
+    /// @param allow_answer_park Indicates if parking a packet is allowed.
     /// @return A pointer to the response.
     Pkt4Ptr processLocalizedQuery4(AllocEngine::ClientContext4Ptr& ctx,
-                                   bool allow_packet_park);
+                                   bool allow_answer_park);
+
+    /// @brief Process a localized incoming DHCPv4 query.
+    ///
+    /// It calls per-type processXXX methods, generates appropriate answer,
+    /// sends the answer to the client.
+    ///
+    /// @param query A pointer to the unparked packet.
+    /// @param ctx Pointer to The client context.
+    /// @param allow_answer_park Indicates if parking a packet is allowed.
+    /// @return A pointer to the response.
+    void processLocalizedQuery4AndSendResponse(Pkt4Ptr query,
+                                               AllocEngine::ClientContext4Ptr& ctx,
+                                               bool allow_answer_park);
 
     /// @brief Process a localized incoming DHCPv4 query.
     ///
@@ -409,10 +422,10 @@ public:
     /// for packets parked in the subnet4_select callout.
     ///
     /// @param query A pointer to the unparked packet.
-    /// @param ctx Pointer to The client context.
+    /// @param allow_answer_park Indicates if parking a packet is allowed.
     /// @return A pointer to the response.
     void processLocalizedQuery4AndSendResponse(Pkt4Ptr query,
-                                               AllocEngine::ClientContext4Ptr& ctx);
+                                               bool allow_answer_park);
 
     /// @brief Instructs the server to shut down.
     void shutdown() override;
@@ -531,7 +544,7 @@ protected:
     ///
     /// @return true if the message should be further processed, or false if
     /// the message should be discarded.
-    bool accept(const Pkt4Ptr& query) const;
+    bool accept(const Pkt4Ptr& query);
 
     /// @brief Check if a message sent by directly connected client should be
     /// accepted or discarded.
@@ -560,7 +573,7 @@ protected:
     ///
     /// @return true if message is accepted for further processing, false
     /// otherwise.
-    bool acceptDirectRequest(const Pkt4Ptr& query) const;
+    bool acceptDirectRequest(const Pkt4Ptr& query);
 
     /// @brief Check if received message type is valid for the server to
     /// process.
@@ -1079,10 +1092,12 @@ protected:
     /// @param query client's message
     /// @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)
     isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& query,
                                        bool& drop,
-                                       bool sanity_only = false) const;
+                                       bool sanity_only = false,
+                                       bool allow_answer_park = true);
 
     /// @brief Selects a subnet for a given client's DHCP4o6 packet.
     ///
@@ -1094,10 +1109,12 @@ protected:
     /// @param query client's message
     /// @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)
     isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr& query,
                                           bool& drop,
-                                          bool sanity_only = false) const;
+                                          bool sanity_only = false,
+                                          bool allow_answer_park = true);
 
     /// @brief dummy wrapper around IfaceMgr::receive4
     ///