]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3683] Checkpoint (2)
authorFrancis Dupont <fdupont@isc.org>
Sun, 23 Feb 2025 01:03:18 +0000 (02:03 +0100)
committerFrancis Dupont <fdupont@isc.org>
Tue, 4 Mar 2025 21:49:12 +0000 (22:49 +0100)
src/bin/dhcp6/dhcp6_hooks.dox
src/bin/dhcp6/dhcp6_messages.cc
src/bin/dhcp6/dhcp6_messages.h
src/bin/dhcp6/dhcp6_messages.mes
src/bin/dhcp6/dhcp6_srv.cc
tools/exhonerated-duplicate-messages.txt

index d91aca8d2feecd3b1cc6eecd6381618b103f1f56..c660305f6e27fbd80817bc8e20545271c53892ec 100644 (file)
@@ -345,6 +345,23 @@ called before "subnet6_select".
 
  - <b>Next step status</b>: Not applicable, its value will be ignored.
 
+@subsection dhcpv6HookRegister6 register6
+
+ - @b Arguments:
+  - name: @b query6, type: isc::dhcp::Pkt6Ptr, direction: <b>in</b>
+  - name: @b response6, type: isc::dhcp::Pkt6Ptr, direction: <b>in</b>
+  - name: @b address6, type: isc::asiolink::IOAddress, direction: <b>in</b>
+  - name: @b old_lease6, type: isc::dhcp::Lease6Ptr, direction: <b>in</b>
+  - name: @b new_lease6, type: isc::dhcp::Lease6Ptr, direction: <b>in/out<b>
+
+ - @b Description: this callout is executed when an addr-reg-inform was
+   received and successfully processed. When the new_lease is cleared
+   this instructs the server to make stateless registration i.e. address
+   registration is used only for side effects and does not maintain state.
+
+ - <b>Next step status</b>: If any callout sets the status to SKIP or DROP,
+   the server will cancel current packet processing.
+
 @subsection dhcpv6Leases6Committed leases6_committed
 
  - @b Arguments:
index 641b44f5e754dac79d27a041347bc0bcb70f764a..d883dd70bd75a76ca451021930cfa7d1079e4cd3 100644 (file)
@@ -13,6 +13,8 @@ extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_RESULT = "DHCP6_ADD
 extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_NO_TEST = "DHCP6_ADDITIONAL_CLASS_NO_TEST";
 extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_UNDEFINED = "DHCP6_ADDITIONAL_CLASS_UNDEFINED";
 extern const isc::log::MessageID DHCP6_ADD_GLOBAL_STATUS_CODE = "DHCP6_ADD_GLOBAL_STATUS_CODE";
+extern const isc::log::MessageID DHCP6_ADD_REG_INFORM_CLIENT_CHANGE = "DHCP6_ADD_REG_INFORM_CLIENT_CHANGE";
+extern const isc::log::MessageID DHCP6_ADD_REG_INFORM_FAIL = "DHCP6_ADD_REG_INFORM_FAIL";
 extern const isc::log::MessageID DHCP6_ADD_STATUS_CODE_FOR_IA = "DHCP6_ADD_STATUS_CODE_FOR_IA";
 extern const isc::log::MessageID DHCP6_ALREADY_RUNNING = "DHCP6_ALREADY_RUNNING";
 extern const isc::log::MessageID DHCP6_BUFFER_RECEIVED = "DHCP6_BUFFER_RECEIVED";
@@ -77,6 +79,7 @@ extern const isc::log::MessageID DHCP6_HOOK_LEASES6_PARKING_LOT_FULL = "DHCP6_HO
 extern const isc::log::MessageID DHCP6_HOOK_PACKET_RCVD_SKIP = "DHCP6_HOOK_PACKET_RCVD_SKIP";
 extern const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_DROP = "DHCP6_HOOK_PACKET_SEND_DROP";
 extern const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_SKIP = "DHCP6_HOOK_PACKET_SEND_SKIP";
+extern const isc::log::MessageID DHCP6_HOOK_REGISTER6_SKIP = "DHCP6_HOOK_REGISTER6_SKIP";
 extern const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_DROP = "DHCP6_HOOK_SUBNET6_SELECT_DROP";
 extern const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_PARK = "DHCP6_HOOK_SUBNET6_SELECT_PARK";
 extern const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_SKIP = "DHCP6_HOOK_SUBNET6_SELECT_SKIP";
@@ -180,6 +183,8 @@ const char* values[] = {
     "DHCP6_ADDITIONAL_CLASS_NO_TEST", "additional class %1 has no test expression, adding it to client's classes unconditionally",
     "DHCP6_ADDITIONAL_CLASS_UNDEFINED", "additional class %1 has no definition",
     "DHCP6_ADD_GLOBAL_STATUS_CODE", "%1: adding Status Code to DHCPv6 packet: %2",
+    "DHCP6_ADD_REG_INFORM_CLIENT_CHANGE", "received an add-reg-inform for %1 from client '%2' but the address was registered by another client '%3'",
+    "DHCP6_ADD_REG_INFORM_FAIL", "error on add-reg-inform from client %1: %2",
     "DHCP6_ADD_STATUS_CODE_FOR_IA", "%1: adding Status Code to IA with iaid=%2: %3",
     "DHCP6_ALREADY_RUNNING", "%1 already running? %2",
     "DHCP6_BUFFER_RECEIVED", "received buffer from %1:%2 to %3:%4 over interface %5",
@@ -244,6 +249,7 @@ const char* values[] = {
     "DHCP6_HOOK_PACKET_RCVD_SKIP", "%1: packet is dropped, because a callout set the next step to SKIP",
     "DHCP6_HOOK_PACKET_SEND_DROP", "%1: prepared DHCPv6 response was not sent because a callout set the next ste to DROP",
     "DHCP6_HOOK_PACKET_SEND_SKIP", "%1: prepared DHCPv6 response is not built because a callout set the next step to SKIP",
+    "DHCP6_HOOK_REGISTER6_SKIP", "%1: add-reg-inform for %2 is dropped, because a callout set the next step to SKIP",
     "DHCP6_HOOK_SUBNET6_SELECT_DROP", "%1: packet was dropped because a callout set the drop flag",
     "DHCP6_HOOK_SUBNET6_SELECT_PARK", "%1: packet was parked",
     "DHCP6_HOOK_SUBNET6_SELECT_SKIP", "%1: no subnet was selected because a callout set the next step to SKIP",
index 935f4a681107ce6ca16d5336d1617d41a8264f46..127ebbf9f8f5f636d09b09cbc47fb0972b4a12d7 100644 (file)
@@ -14,6 +14,8 @@ extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_EVAL_RESULT;
 extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_NO_TEST;
 extern const isc::log::MessageID DHCP6_ADDITIONAL_CLASS_UNDEFINED;
 extern const isc::log::MessageID DHCP6_ADD_GLOBAL_STATUS_CODE;
+extern const isc::log::MessageID DHCP6_ADD_REG_INFORM_CLIENT_CHANGE;
+extern const isc::log::MessageID DHCP6_ADD_REG_INFORM_FAIL;
 extern const isc::log::MessageID DHCP6_ADD_STATUS_CODE_FOR_IA;
 extern const isc::log::MessageID DHCP6_ALREADY_RUNNING;
 extern const isc::log::MessageID DHCP6_BUFFER_RECEIVED;
@@ -78,6 +80,7 @@ extern const isc::log::MessageID DHCP6_HOOK_LEASES6_PARKING_LOT_FULL;
 extern const isc::log::MessageID DHCP6_HOOK_PACKET_RCVD_SKIP;
 extern const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_DROP;
 extern const isc::log::MessageID DHCP6_HOOK_PACKET_SEND_SKIP;
+extern const isc::log::MessageID DHCP6_HOOK_REGISTER6_SKIP;
 extern const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_DROP;
 extern const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_PARK;
 extern const isc::log::MessageID DHCP6_HOOK_SUBNET6_SELECT_SKIP;
index 978433b8a93ce5f9ae838d9f257e7ee2c7706d81..caa1fd16b66389550c8909f7ed0229a791bd214f 100644 (file)
@@ -44,6 +44,16 @@ Status Code option. The first argument includes the client and the
 transaction identification information. The second argument includes
 the details of the status code.
 
+% DHCP6_ADD_REG_INFORM_FAIL error on add-reg-inform from client %1: %2
+This information message is issued when the processing of an add-reg-inform
+message failed. The address of the client, usually also the address to
+register, and the description of the problem are printed.
+
+% DHCP6_ADD_REG_INFORM_CLIENT_CHANGE received an add-reg-inform for %1 from client '%2' but the address was registered by another client '%3'
+This information message is issued when a lease for another client already
+exists for an address being registered. The address, the new client and
+previous client identifiers are printed.
+
 % DHCP6_ADD_STATUS_CODE_FOR_IA %1: adding Status Code to IA with iaid=%2: %3
 Logged at debug log level 50.
 This message is logged when the server is adding the Status Code
@@ -484,6 +494,13 @@ not build the wire data (pack) because it was already done by the
 book. The argument specifies the client and transaction identification
 information.
 
+% DHCP6_HOOK_REGISTER6_SKIP %1: add-reg-inform for %2 is dropped, because a callout set the next step to SKIP
+Logged at debug log level 40.
+This debug message is printed when a callout installed on the register6
+hook point sets the next step to SKIP. For this particular hook point, the
+value setting instructs the server to cancel the address registration and
+drop the packet.
+
 % DHCP6_HOOK_SUBNET6_SELECT_DROP %1: packet was dropped because a callout set the drop flag
 Logged at debug log level 40.
 This debug message is printed when a callout installed on the
index 99eb899959a774c1ee7824922638caf03cb39111..324579e5a1395ba0dae8cea50c8c3f65a5aa3216 100644 (file)
@@ -145,6 +145,7 @@ struct Dhcp6Hooks {
     int hook_index_lease6_decline_;   ///< index for "lease6_decline" hook point
     int hook_index_host6_identifier_; ///< index for "host6_identifier" hook point
     int hook_index_ddns6_update_;     ///< index for "ddns6_update" hook point
+    int hook_index_register6_;        ///< index for "register6" hook point
 
     /// Constructor that registers hook points for DHCPv6 engine
     Dhcp6Hooks() {
@@ -158,6 +159,7 @@ struct Dhcp6Hooks {
         hook_index_lease6_decline_    = HooksManager::registerHook("lease6_decline");
         hook_index_host6_identifier_  = HooksManager::registerHook("host6_identifier");
         hook_index_ddns6_update_      = HooksManager::registerHook("ddns6_update");
+        hook_index_register6_         = HooksManager::registerHook("register6");
     }
 };
 
@@ -4513,6 +4515,7 @@ Dhcpv6Srv::processAddRegInform(AllocEngine::ClientContext6& ctx) {
         ctx.createIAContext();
         ctx.currentIA().type_ = Lease::TYPE_NA;
         ctx.currentIA().iaid_ = ia->getIAID();
+        ctx.currentIA().ia_rsp_ = ia;
 
         // Get IAADDR from the IA_NA. There must be the only option.
         OptionCollection iaaddrs = ia->getOptions();
@@ -4524,7 +4527,6 @@ Dhcpv6Srv::processAddRegInform(AllocEngine::ClientContext6& ctx) {
         if (!iaaddr) {
             isc_throw(RFCViolation, "Can't get the IAADDR sub-option");
         }
-        ctx.currentIA().addHint(iaaddr);
 
         // Client and IADDR addresses must match.
         if (addr != iaaddr->getAddress()) {
@@ -4554,13 +4556,27 @@ Dhcpv6Srv::processAddRegInform(AllocEngine::ClientContext6& ctx) {
             isc_throw(RFCViolation, "Address " << addr << " is reserved");
         }
     } catch (const std::exception &ex) {
-        // log
+        // Incoming processing failed.
+        LOG_INFO(packet6_logger, DHCP6_ADD_REG_INFORM_FAIL)
+            .arg(addr)
+            .arg(ex.what());
         return (Pkt6Ptr());
     }
 
+    // Record if it is a registration renewal.
+    bool renewal = !!old_lease;
+
     // Check if the client is the same.
-    if (old_lease && old_lease->duid_ && *ctx.duid_ != *(old_lease->duid_)) {
-        // log
+    if (old_lease) {
+        if (old_lease->duid_ && (*ctx.duid_ != *(old_lease->duid_))) {
+            LOG_INFO(packet6_logger, DHCP6_ADD_REG_INFORM_CLIENT_CHANGE)
+                .arg(addr)
+                .arg(ctx.duid_->toText())
+                .arg(old_lease->duid_->toText());
+        }
+        if (old_lease->subnet_id_ != subnet->getID()) {
+            renewal = false;
+        }
     }
 
     // Build response.
@@ -4597,37 +4613,83 @@ Dhcpv6Srv::processAddRegInform(AllocEngine::ClientContext6& ctx) {
     appendRequestedOptions(add_reg_inf, add_reg_rep, co_list);
     appendRequestedVendorOptions(add_reg_inf, add_reg_rep, ctx, co_list);
 
-    // call the new callout
+    // Handle the "register6" callout point.
+    if (HooksManager::calloutsPresent(Hooks.hook_index_register6_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(add_reg_inf);
+        ScopedCalloutHandleState callout_handle_state(callout_handle);
 
-    // Add stats
-    if (old_lease) {
-        // Save the old lease for the lease6_committed callout.
-        ctx.currentIA().old_leases_.push_back(old_lease);
-        if (!lease) {
-            if (!LeaseMgrFactory::instance().deleteLease(old_lease)) {
+        // Pass the query6 argument.
+        ScopedEnableOptionsCopy<Pkt6> query6_options_copy(add_reg_inf);
+        callout_handle->setArgument("query6", add_reg_inf);
+
+        // Pass the response6 argument.
+        ScopedEnableOptionsCopy<Pkt6> rsp6_options_copy(add_reg_rep);
+        callout_handle->setArgument("response6", add_reg_rep);
+
+        // Pass the address6 argument.
+        callout_handle->setArgument("address6", addr);
+
+        // Pass the old_lease argument.
+        callout_handle->setArgument("old_lease6", old_lease);
+
+        // Pass the new_lease argument.
+        callout_handle->setArgument("new_lease6", lease);
+
+        // Call callouts
+        HooksManager::callCallouts(Hooks.hook_index_register6_, *callout_handle);
+
+        // Callouts decided to skip the next processing step. This means
+        // cancel processing so drop.
+        if ((callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) ||
+            (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP)) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_REGISTER6_SKIP)
+                .arg(add_reg_inf->getLabel())
+                .arg(addr);
+            return (Pkt6Ptr());
+        }
+
+        Lease6Ptr new_lease;
+        callout_handle->getArgument("new_lease6", new_lease);
+        if (!new_lease) {
+            // Stateless registration: skip lease code.
+            lease.reset();
+        }
+    }
+
+    if (lease) {
+        // Statefull registration.
+        if (old_lease) {
+            try {
+                LeaseMgrFactory::instance().updateLease6(lease);
+            } catch (const std::exception&) {
                 // Assume that stats and DNS were handled by someone else.
                 return (add_reg_rep);
             }
+            // Save the old lease for the lease6_committed callout.
+            ctx.currentIA().old_leases_.push_back(old_lease);
+            if (!renewal) {
+                // -1 on stats.
+            } else  {
+                // Save the old lease for the DNS update.
+                ctx.currentIA().changed_leases_.push_back(old_lease);
+            }
         } else {
-            try {
-                LeaseMgrFactory::instance().updateLease6(lease);
-            } catch (const std::exception&) {
+            if (!LeaseMgrFactory::instance().addLease(lease)) {
                 // Assume that stats and DNS were handled by someone else.
                 return (add_reg_rep);
             }
         }
-    } else if (lease) {
-        if (!LeaseMgrFactory::instance().addLease(lease)) {
-            // Assume that stats and DNS were handled by someone else.
-            return (add_reg_rep);
-        }
-    }
-    if (lease) {
         // Save the new lease for the lease6_committed callout.
         ctx.new_leases_.push_back(lease);
+        if (!renewal) {
+            // +1 on stats
+        }
     }
 
+    // Deal with FQDN.
     updateReservedFqdn(ctx, add_reg_rep);
+    generateFqdn(add_reg_rep, ctx);
+    createNameChangeRequests(add_reg_rep, ctx);
 
     return (add_reg_rep);
 }
index ac5136d27d652e79c703a2b80d0cca31163fabba..48e092cedf8f78256b4ba433bffeb460aac84ab1 100644 (file)
@@ -8,7 +8,7 @@
     % DHCP4_CLASS_ASSIGNED: 3
     % DHCP4_PACKET_QUEUE_FULL: 2
     % DHCP4_CONFIG_RECEIVED: 2
-    % DHCP6_CLASSES_ASSIGNED: 8
+    % DHCP6_CLASSES_ASSIGNED: 9
     % DHCP6_CLASS_ASSIGNED: 3
     % DHCP6_PACKET_QUEUE_FULL: 2
     % DHCP6_CONFIG_RECEIVED: 2