Browse Source

[master] Merged trac4267 (final run_one())

Francis Dupont 9 years ago
parent
commit
2047a44ad5

+ 1 - 0
AUTHORS

@@ -89,6 +89,7 @@ We have received the following contributions:
 
  - Jinmei Tatuya
    2015-10: Pkt4o6 class improvements
+   2015-11: split Dhcpv4Srv::run() into run() and processPacket()
 
  - Sebastien Couture, Ubity Inc
    2015-12: Fixes to MySQL schema creation

+ 11 - 4
src/bin/dhcp4/dhcp4_messages.mes

@@ -449,10 +449,17 @@ This error message is issued when preparing an on-wire format of the packet
 has failed. The first argument identifies the client and the DHCP transaction.
 The second argument includes the error string.
 
-% DHCP4_PACKET_PROCESS_EXCEPTION exception occurred during packet processing: %1
-This error message indicates that an exception was raised during packet processing
-that was not caught by other, more specific exception handlers. This packet will
-be dropped and the server will continue operation.
+% DHCP4_PACKET_PROCESS_EXCEPTION exception occurred during packet processing
+This error message indicates that a non-standard exception was raised
+during packet processing that was not caught by other, more specific
+exception handlers. This packet will be dropped and the server will
+continue operation.
+
+% DHCP4_PACKET_PROCESS_STD_EXCEPTION exception occurred during packet processing: %1
+This error message indicates that a standard exception was raised
+during packet processing that was not caught by other, more specific
+exception handlers. This packet will be dropped and the server will
+continue operation.
 
 % DHCP4_PACKET_RECEIVED %1: %2 (type %3) received from %4 to %5 on interface %6
 A debug message noting that the server has received the specified type of

+ 324 - 311
src/bin/dhcp4/dhcp4_srv.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 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
@@ -418,388 +418,401 @@ Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
 bool
 Dhcpv4Srv::run() {
     while (!shutdown_) {
-        // client's message and server's response
-        Pkt4Ptr query;
-        Pkt4Ptr rsp;
-
         try {
+            run_one();
+        } catch (const std::exception& e) {
+            // General catch-all exception that are not caught by more specific
+            // catches. This one is for exceptions derived from std::exception.
+            LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_STD_EXCEPTION)
+                .arg(e.what());
+        } catch (...) {
+            // General catch-all exception that are not caught by more specific
+            // catches. This one is for other exceptions, not derived from
+            // std::exception.
+            LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION);
+        }
+    }
 
-        try {
-            uint32_t timeout = 1000;
-            LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT).arg(timeout);
-            query = receivePacket(timeout);
-
-            // Log if packet has arrived. We can't log the detailed information
-            // about the DHCP message because it hasn't been unpacked/parsed
-            // yet, and it can't be parsed at this point because hooks will
-            // have to process it first. The only information available at this
-            // point are: the interface, source address and destination addresses
-            // and ports.
-            if (query) {
-                LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_BUFFER_RECEIVED)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getRemotePort())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getLocalPort())
-                    .arg(query->getIface());
+    return (true);
+}
 
-            } else {
-                LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT_INTERRUPTED)
-                    .arg(timeout);
-            }
+void
+Dhcpv4Srv::run_one() {
+    // client's message and server's response
+    Pkt4Ptr query;
+    Pkt4Ptr rsp;
 
-        } catch (const SignalInterruptOnSelect) {
-            // Packet reception interrupted because a signal has been received.
-            // This is not an error because we might have received a SIGTERM,
-            // SIGINT, SIGHUP or SIGCHILD which are handled by the server. For
-            // signals that are not handled by the server we rely on the default
-            // behavior of the system.
-            LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT_SIGNAL)
-                .arg(signal_set_->getNext());
-        } catch (const std::exception& e) {
-            // Log all other errors.
-            LOG_ERROR(packet4_logger, DHCP4_BUFFER_RECEIVE_FAIL).arg(e.what());
-        }
+    try {
+        uint32_t timeout = 1000;
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT).arg(timeout);
+        query = receivePacket(timeout);
+
+        // Log if packet has arrived. We can't log the detailed information
+        // about the DHCP message because it hasn't been unpacked/parsed
+        // yet, and it can't be parsed at this point because hooks will
+        // have to process it first. The only information available at this
+        // point are: the interface, source address and destination addresses
+        // and ports.
+        if (query) {
+            LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_BUFFER_RECEIVED)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getRemotePort())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getLocalPort())
+                .arg(query->getIface());
 
-        // Handle next signal received by the process. It must be called after
-        // an attempt to receive a packet to properly handle server shut down.
-        // The SIGTERM or SIGINT will be received prior to, or during execution
-        // of select() (select is invoked by receivePacket()). When that
-        // happens, select will be interrupted. The signal handler will be
-        // invoked immediately after select(). The handler will set the
-        // shutdown flag and cause the process to terminate before the next
-        // select() function is called. If the function was called before
-        // receivePacket the process could wait up to the duration of timeout
-        // of select() to terminate.
-        try {
-            handleSignal();
-        } catch (const std::exception& e) {
-            // Standard exception occurred. Let's be on the safe side to
-            // catch std::exception.
-            LOG_ERROR(dhcp4_logger, DHCP4_HANDLE_SIGNAL_EXCEPTION)
-                .arg(e.what());
+        } else {
+            LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT_INTERRUPTED)
+                .arg(timeout);
         }
 
-        // Timeout may be reached or signal received, which breaks select()
-        // with no reception occurred. No need to log anything here because
-        // we have logged right after the call to receivePacket().
-        if (!query) {
-            continue;
-        }
+    } catch (const SignalInterruptOnSelect) {
+        // Packet reception interrupted because a signal has been received.
+        // This is not an error because we might have received a SIGTERM,
+        // SIGINT, SIGHUP or SIGCHILD which are handled by the server. For
+        // signals that are not handled by the server we rely on the default
+        // behavior of the system.
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT_SIGNAL)
+            .arg(signal_set_->getNext());
+    } catch (const std::exception& e) {
+        // Log all other errors.
+        LOG_ERROR(packet4_logger, DHCP4_BUFFER_RECEIVE_FAIL).arg(e.what());
+    }
+
+    // Handle next signal received by the process. It must be called after
+    // an attempt to receive a packet to properly handle server shut down.
+    // The SIGTERM or SIGINT will be received prior to, or during execution
+    // of select() (select is invoked by receivePacket()). When that
+    // happens, select will be interrupted. The signal handler will be
+    // invoked immediately after select(). The handler will set the
+    // shutdown flag and cause the process to terminate before the next
+    // select() function is called. If the function was called before
+    // receivePacket the process could wait up to the duration of timeout
+    // of select() to terminate.
+    try {
+        handleSignal();
+    } catch (const std::exception& e) {
+        // Standard exception occurred. Let's be on the safe side to
+        // catch std::exception.
+        LOG_ERROR(dhcp4_logger, DHCP4_HANDLE_SIGNAL_EXCEPTION)
+            .arg(e.what());
+    }
 
-        // Log reception of the packet. We need to increase it early, as any
-        // failures in unpacking will cause the packet to be dropped. We
-        // will increase type specific packets further down the road.
-        // See processStatsReceived().
-        isc::stats::StatsMgr::instance().addValue("pkt4-received",
-                                                  static_cast<int64_t>(1));
+    // Timeout may be reached or signal received, which breaks select()
+    // with no reception occurred. No need to log anything here because
+    // we have logged right after the call to receivePacket().
+    if (!query) {
+        return;
+    }
 
-        // In order to parse the DHCP options, the server needs to use some
-        // configuration information such as: existing option spaces, option
-        // definitions etc. This is the kind of information which is not
-        // available in the libdhcp, so we need to supply our own implementation
-        // of the option parsing function here, which would rely on the
-        // configuration data.
-        query->setCallback(boost::bind(&Dhcpv4Srv::unpackOptions, this,
-                                       _1, _2, _3));
+    processPacket(query, rsp);
 
-        bool skip_unpack = false;
+    if (!rsp) {
+        return;
+    }
 
-        // The packet has just been received so contains the uninterpreted wire
-        // data; execute callouts registered for buffer4_receive.
-        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
+    try {
+        // Now all fields and options are constructed into output wire buffer.
+        // Option objects modification does not make sense anymore. Hooks
+        // can only manipulate wire buffer at this stage.
+        // Let's execute all callouts registered for buffer4_send
+        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
             // Delete previously set arguments
             callout_handle->deleteAllArguments();
 
             // Pass incoming packet as argument
-            callout_handle->setArgument("query4", query);
+            callout_handle->setArgument("response4", rsp);
 
             // Call callouts
-            HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
+            HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
                                        *callout_handle);
 
             // Callouts decided to skip the next processing step. The next
             // processing step would to parse the packet, so skip at this
-            // stage means that callouts did the parsing already, so server
-            // should skip parsing.
+            // stage means drop.
             if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                LOG_DEBUG(hooks_logger, DBG_DHCP4_DETAIL, DHCP4_HOOK_BUFFER_RCVD_SKIP)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getIface());
-                skip_unpack = true;
+                LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
+                          DHCP4_HOOK_BUFFER_SEND_SKIP)
+                    .arg(rsp->getLabel());
+                return;
             }
 
-            callout_handle->getArgument("query4", query);
-
-            /// @todo: add support for DROP status
-        }
+            /// @todo: Add support for DROP status.
 
-        // Unpack the packet information unless the buffer4_receive callouts
-        // indicated they did it
-        if (!skip_unpack) {
-            try {
-                LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_UNPACK)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getIface());
-                query->unpack();
-            } catch (const std::exception& e) {
-                // Failed to parse the packet.
-                LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL,
-                          DHCP4_PACKET_DROP_0001)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getIface())
-                    .arg(e.what());
-
-                // Increase the statistics of parse failues and dropped packets.
-                isc::stats::StatsMgr::instance().addValue("pkt4-parse-failed",
-                                                          static_cast<int64_t>(1));
-                isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
-                                                          static_cast<int64_t>(1));
-                continue;
-            }
+            callout_handle->getArgument("response4", rsp);
         }
 
-        // Update statistics accordingly for received packet.
-        processStatsReceived(query);
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_SEND)
+            .arg(rsp->getLabel())
+            .arg(rsp->getName())
+            .arg(static_cast<int>(rsp->getType()))
+            .arg(rsp->getLocalAddr())
+            .arg(rsp->getLocalPort())
+            .arg(rsp->getRemoteAddr())
+            .arg(rsp->getRemotePort())
+            .arg(rsp->getIface());
+
+        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA,
+                  DHCP4_RESPONSE_DATA)
+            .arg(rsp->getLabel())
+            .arg(rsp->getName())
+            .arg(static_cast<int>(rsp->getType()))
+            .arg(rsp->toText());
+        sendPacket(rsp);
+
+        // Update statistics accordingly for sent packet.
+        processStatsSent(rsp);
+
+    } catch (const std::exception& e) {
+        LOG_ERROR(packet4_logger, DHCP4_PACKET_SEND_FAIL)
+            .arg(rsp->getLabel())
+            .arg(e.what());
+    }
+}
 
-        // 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.
-        classifyPacket(query);
+void
+Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
+    // Log reception of the packet. We need to increase it early, as any
+    // failures in unpacking will cause the packet to be dropped. We
+    // will increase type specific statistic further down the road.
+    // See processStatsReceived().
+    isc::stats::StatsMgr::instance().addValue("pkt4-received",
+                                              static_cast<int64_t>(1));
 
-        // Check whether the message should be further processed or discarded.
-        // There is no need to log anything here. This function logs by itself.
-        if (!accept(query)) {
-            // Increase the statistic of dropped packets.
-            isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
-                                                      static_cast<int64_t>(1));
-            continue;
-        }
+    // In order to parse the DHCP options, the server needs to use some
+    // configuration information such as: existing option spaces, option
+    // definitions etc. This is the kind of information which is not
+    // available in the libdhcp, so we need to supply our own implementation
+    // of the option parsing function here, which would rely on the
+    // configuration data.
+    query->setCallback(boost::bind(&Dhcpv4Srv::unpackOptions, this,
+                                   _1, _2, _3));
 
-        // We have sanity checked (in accept() that the Message Type option
-        // exists, so we can safely get it here.
-        int type = query->getType();
-        LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_PACKET_RECEIVED)
-            .arg(query->getLabel())
-            .arg(query->getName())
-            .arg(type)
-            .arg(query->getRemoteAddr())
-            .arg(query->getLocalAddr())
-            .arg(query->getIface());
-        LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
-            .arg(query->getLabel())
-            .arg(query->toText());
+    bool skip_unpack = false;
 
-        // Let's execute all callouts registered for pkt4_receive
-        if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
-            CalloutHandlePtr callout_handle = getCalloutHandle(query);
+    // The packet has just been received so contains the uninterpreted wire
+    // data; execute callouts registered for buffer4_receive.
+    if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
-            // Delete previously set arguments
-            callout_handle->deleteAllArguments();
+        // Delete previously set arguments
+        callout_handle->deleteAllArguments();
 
-            // Pass incoming packet as argument
-            callout_handle->setArgument("query4", query);
+        // Pass incoming packet as argument
+        callout_handle->setArgument("query4", query);
 
-            // Call callouts
-            HooksManager::callCallouts(hook_index_pkt4_receive_,
-                                       *callout_handle);
+        // Call callouts
+        HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
+                                   *callout_handle);
 
-            // Callouts decided to skip the next processing step. The next
-            // processing step would to process the packet, so skip at this
-            // stage means drop.
-            if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP)
-                    .arg(query->getLabel());
-                continue;
-            }
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to parse the packet, so skip at this
+        // stage means that callouts did the parsing already, so server
+        // should skip parsing.
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP4_DETAIL,
+                      DHCP4_HOOK_BUFFER_RCVD_SKIP)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getIface());
+            skip_unpack = true;
+        }
 
-            /// @todo: Add support for DROP status
+        callout_handle->getArgument("query4", query);
 
-            callout_handle->getArgument("query4", query);
-        }
+        /// @todo: add support for DROP status
+    }
 
+    // Unpack the packet information unless the buffer4_receive callouts
+    // indicated they did it
+    if (!skip_unpack) {
         try {
-            switch (query->getType()) {
-            case DHCPDISCOVER:
-                rsp = processDiscover(query);
-                break;
-
-            case DHCPREQUEST:
-                // Note that REQUEST is used for many things in DHCPv4: for
-                // requesting new leases, renewing existing ones and even
-                // for rebinding.
-                rsp = processRequest(query);
-                break;
-
-            case DHCPRELEASE:
-                processRelease(query);
-                break;
-
-            case DHCPDECLINE:
-                processDecline(query);
-                break;
-
-            case DHCPINFORM:
-                rsp = processInform(query);
-                break;
-
-            default:
-                // Only action is to output a message if debug is enabled,
-                // and that is covered by the debug statement before the
-                // "switch" statement.
-                ;
-            }
+            LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_UNPACK)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getIface());
+            query->unpack();
         } catch (const std::exception& e) {
-
-            // Catch-all exception (we used to call only isc::Exception, but
-            // std::exception could potentially be raised and if we don't catch
-            // it here, it would be caught in main() and the process would
-            // terminate).  Just log the problem and ignore the packet.
-            // (The problem is logged as a debug message because debug is
-            // disabled by default - it prevents a DDOS attack based on the
-            // sending of problem packets.)
-            LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_BASIC,
-                      DHCP4_PACKET_DROP_0007)
-                .arg(query->getLabel())
+            // Failed to parse the packet.
+            LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_DETAIL,
+                      DHCP4_PACKET_DROP_0001)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getIface())
                 .arg(e.what());
 
-            // Increase the statistic of dropped packets.
+            // Increase the statistics of parse failues and dropped packets.
+            isc::stats::StatsMgr::instance().addValue("pkt4-parse-failed",
+                                                      static_cast<int64_t>(1));
             isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
                                                       static_cast<int64_t>(1));
+            return;
         }
+    }
 
-        if (!rsp) {
-            continue;
-        }
+    // Update statistics accordingly for received packet.
+    processStatsReceived(query);
+
+    // 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.
+    classifyPacket(query);
 
+    // Check whether the message should be further processed or discarded.
+    // There is no need to log anything here. This function logs by itself.
+    if (!accept(query)) {
+        // Increase the statistic of dropped packets.
+        isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
+                                                  static_cast<int64_t>(1));
+        return;
+    }
 
-        // Specifies if server should do the packing
-        bool skip_pack = false;
+    // We have sanity checked (in accept() that the Message Type option
+    // exists, so we can safely get it here.
+    int type = query->getType();
+    LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC_DATA, DHCP4_PACKET_RECEIVED)
+        .arg(query->getLabel())
+        .arg(query->getName())
+        .arg(type)
+        .arg(query->getRemoteAddr())
+        .arg(query->getLocalAddr())
+        .arg(query->getIface());
+    LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
+        .arg(query->getLabel())
+        .arg(query->toText());
+
+    // Let's execute all callouts registered for pkt4_receive
+    if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
-        // Execute all callouts registered for pkt4_send
-        if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
-            CalloutHandlePtr callout_handle = getCalloutHandle(query);
+        // Delete previously set arguments
+        callout_handle->deleteAllArguments();
 
-            // Delete all previous arguments
-            callout_handle->deleteAllArguments();
+        // Pass incoming packet as argument
+        callout_handle->setArgument("query4", query);
 
-            // Clear skip flag if it was set in previous callouts
-            callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+        // Call callouts
+        HooksManager::callCallouts(hook_index_pkt4_receive_,
+                                   *callout_handle);
 
-            // Set our response
-            callout_handle->setArgument("response4", rsp);
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to process the packet, so skip at this
+        // stage means drop.
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
+                      DHCP4_HOOK_PACKET_RCVD_SKIP)
+                .arg(query->getLabel());
+            return;
+        }
 
-            // Also pass the corresponding query packet as argument
-            callout_handle->setArgument("query4", query);
+        /// @todo: Add support for DROP status
 
-            // Call all installed callouts
-            HooksManager::callCallouts(hook_index_pkt4_send_,
-                                       *callout_handle);
+        callout_handle->getArgument("query4", query);
+    }
 
-            // Callouts decided to skip the next processing step. The next
-            // processing step would to send the packet, so skip at this
-            // stage means "drop response".
-            if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP)
-                    .arg(query->getLabel());
-                skip_pack = true;
-            }
+    try {
+        switch (query->getType()) {
+        case DHCPDISCOVER:
+            rsp = processDiscover(query);
+            break;
 
-            /// @todo: Add support for DROP status
-        }
+        case DHCPREQUEST:
+            // Note that REQUEST is used for many things in DHCPv4: for
+            // requesting new leases, renewing existing ones and even
+            // for rebinding.
+            rsp = processRequest(query);
+            break;
 
-        if (!skip_pack) {
-            try {
-                LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_PACK)
-                    .arg(rsp->getLabel());
-                rsp->pack();
-            } catch (const std::exception& e) {
-                LOG_ERROR(options4_logger, DHCP4_PACKET_PACK_FAIL)
-                    .arg(rsp->getLabel())
-                    .arg(e.what());
-            }
+        case DHCPRELEASE:
+            processRelease(query);
+            break;
+
+        case DHCPDECLINE:
+            processDecline(query);
+            break;
+
+        case DHCPINFORM:
+            rsp = processInform(query);
+            break;
+
+        default:
+            // Only action is to output a message if debug is enabled,
+            // and that is covered by the debug statement before the
+            // "switch" statement.
+            ;
         }
+    } catch (const std::exception& e) {
+
+        // Catch-all exception (we used to call only isc::Exception, but
+        // std::exception could potentially be raised and if we don't catch
+        // it here, it would be caught in main() and the process would
+        // terminate).  Just log the problem and ignore the packet.
+        // (The problem is logged as a debug message because debug is
+        // disabled by default - it prevents a DDOS attack based on the
+        // sending of problem packets.)
+        LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_BASIC,
+                  DHCP4_PACKET_DROP_0007)
+            .arg(query->getLabel())
+            .arg(e.what());
 
-        try {
-            // Now all fields and options are constructed into output wire buffer.
-            // Option objects modification does not make sense anymore. Hooks
-            // can only manipulate wire buffer at this stage.
-            // Let's execute all callouts registered for buffer4_send
-            if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
-                CalloutHandlePtr callout_handle = getCalloutHandle(query);
-
-                // Delete previously set arguments
-                callout_handle->deleteAllArguments();
-
-                // Pass incoming packet as argument
-                callout_handle->setArgument("response4", rsp);
-
-                // Call callouts
-                HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
-                                           *callout_handle);
-
-                // Callouts decided to skip the next processing step. The next
-                // processing step would to parse the packet, so skip at this
-                // stage means drop.
-                if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                    LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
-                              DHCP4_HOOK_BUFFER_SEND_SKIP)
-                        .arg(rsp->getLabel());
-                    continue;
-                }
+        // Increase the statistic of dropped packets.
+        isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
+                                                  static_cast<int64_t>(1));
+    }
 
-                /// @todo: Add support for DROP status.
+    if (!rsp) {
+        return;
+    }
 
-                callout_handle->getArgument("response4", rsp);
-            }
+    // Specifies if server should do the packing
+    bool skip_pack = false;
 
-            LOG_DEBUG(packet4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_SEND)
-                .arg(rsp->getLabel())
-                .arg(rsp->getName())
-                .arg(static_cast<int>(rsp->getType()))
-                .arg(rsp->getLocalAddr())
-                .arg(rsp->getLocalPort())
-                .arg(rsp->getRemoteAddr())
-                .arg(rsp->getRemotePort())
-                .arg(rsp->getIface());
-
-            LOG_DEBUG(packet4_logger, DBG_DHCP4_DETAIL_DATA,
-                      DHCP4_RESPONSE_DATA)
-                .arg(rsp->getLabel())
-                .arg(rsp->getName())
-                .arg(static_cast<int>(rsp->getType()))
-                .arg(rsp->toText());
-            sendPacket(rsp);
+    // Execute all callouts registered for pkt4_send
+    if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
-            // Update statistics accordingly for sent packet.
-            processStatsSent(rsp);
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
 
-        } catch (const std::exception& e) {
-            LOG_ERROR(packet4_logger, DHCP4_PACKET_SEND_FAIL)
-                .arg(rsp->getLabel())
-                .arg(e.what());
+        // Clear skip flag if it was set in previous callouts
+        callout_handle->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+
+        // Set our response
+        callout_handle->setArgument("response4", rsp);
+
+        // Also pass the corresponding query packet as argument
+        callout_handle->setArgument("query4", query);
+
+        // Call all installed callouts
+        HooksManager::callCallouts(hook_index_pkt4_send_,
+                                   *callout_handle);
+
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to send the packet, so skip at this
+        // stage means "drop response".
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP4_HOOKS,
+                      DHCP4_HOOK_PACKET_SEND_SKIP)
+                .arg(query->getLabel());
+            skip_pack = true;
         }
 
+        /// @todo: Add support for DROP status
+    }
+
+    if (!skip_pack) {
+        try {
+            LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_PACK)
+                .arg(rsp->getLabel());
+            rsp->pack();
         } catch (const std::exception& e) {
-            // General catch-all exception that are not caught by more specific
-            // catches. This one is for exceptions derived from std::exception.
-            LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION)
+            LOG_ERROR(options4_logger, DHCP4_PACKET_PACK_FAIL)
+                .arg(rsp->getLabel())
                 .arg(e.what());
-        } catch (...) {
-            // General catch-all exception that are not caught by more specific
-            // catches. This one is for other exceptions, not derived from
-            // std::exception.
-            LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION)
-                .arg("an unknown exception not derived from std::exception");
         }
     }
-
-    return (true);
 }
 
 string

+ 20 - 6
src/bin/dhcp4/dhcp4_srv.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 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
@@ -208,14 +208,28 @@ public:
  
     /// @brief Main server processing loop.
     ///
-    /// Main server processing loop. Receives incoming packets, verifies
-    /// their correctness, generates appropriate answer (if needed) and
-    /// transmits responses.
+    /// Main server processing loop. Call the processing step routine
+    /// until shut down.
     ///
-    /// @return true, if being shut down gracefully, fail if experienced
-    ///         critical error.
+    /// @return true, if being shut down gracefully, never fail.
     bool run();
 
+    /// @brief Main server processing step.
+    ///
+    /// Main server processing step. Receives one incoming packet, calls
+    /// the processing packet routing and (if necessary) transmits
+    /// a response.
+    void run_one();
+
+    /// @brief Process a single incoming DHCPv4 packet.
+    ///
+    /// It verifies correctness of the passed packet, call per-type processXXX
+    /// methods, generates appropriate answer.
+    ///
+    /// @param query A pointer to the packet to be processed.
+    /// @param rsp A pointer to the response
+    void processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp);
+
     /// @brief Instructs the server to shut down.
     void shutdown();
 

+ 11 - 4
src/bin/dhcp6/dhcp6_messages.mes

@@ -425,16 +425,23 @@ because packets of this type must be sent to multicast. The first argument
 specifies the client and transaction identification information, the
 second argument specifies packet type.
 
-% DHCP6_PACKET_PROCESS_EXCEPTION exception occurred during packet processing: %1
-This error message indicates that an exception was raised during packet processing
-that was not caught by other, more specific exception handlers. This packet will
-be dropped and the server will continue operation.
+% DHCP6_PACKET_PROCESS_EXCEPTION exception occurred during packet processing
+This error message indicates that a non-standard exception was raised
+during packet processing that was not caught by other, more specific
+exception handlers. This packet will be dropped and the server will
+continue operation.
 
 % DHCP6_PACKET_PROCESS_FAIL processing of %1 message received from %2 failed: %3
 This is a general catch-all message indicating that the processing of the
 specified packet type from the indicated address failed.  The reason is given in the
 message.  The server will not send a response but will instead ignore the packet.
 
+% DHCP6_PACKET_PROCESS_STD_EXCEPTION exception occurred during packet processing: %1
+This error message indicates that a standard exception was raised
+during packet processing that was not caught by other, more specific
+exception handlers. This packet will be dropped and the server will
+continue operation.
+
 % DHCP6_PACKET_RECEIVED %1: %2 (type %3) received from %4 to %5 on interface %6
 A debug message noting that the server has received the specified type of
 packet on the specified interface. The first argument specifies the

+ 345 - 337
src/bin/dhcp6/dhcp6_srv.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 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
@@ -288,420 +288,428 @@ Dhcpv6Srv::createContext(const Pkt6Ptr& pkt) {
 
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
-        // client's message and server's response
-        Pkt6Ptr query;
-        Pkt6Ptr rsp;
-
-        try {
-
         try {
-            uint32_t timeout = 1000;
-            LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT).arg(timeout);
-            query = receivePacket(timeout);
-
-            // Log if packet has arrived. We can't log the detailed information
-            // about the DHCP message because it hasn't been unpacked/parsed
-            // yet, and it can't be parsed at this point because hooks will
-            // have to process it first. The only information available at this
-            // point are: the interface, source address and destination addresses
-            // and ports.
-            if (query) {
-                LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC, DHCP6_BUFFER_RECEIVED)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getRemotePort())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getLocalPort())
-                    .arg(query->getIface());
-
-                // Log reception of the packet. We need to increase it early, as
-                // any failures in unpacking will cause the packet to be dropped.
-                // we will increase type specific packets further down the road.
-                // See processStatsReceived().
-                StatsMgr::instance().addValue("pkt6-received", static_cast<int64_t>(1));
-
-            } else {
-                LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT_INTERRUPTED)
-                    .arg(timeout);
-            }
-
-        } catch (const SignalInterruptOnSelect) {
-            // Packet reception interrupted because a signal has been received.
-            // This is not an error because we might have received a SIGTERM,
-            // SIGINT or SIGHUP which are handled by the server. For signals
-            // that are not handled by the server we rely on the default
-            // behavior of the system.
-            LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT_SIGNAL)
-                .arg(signal_set_->getNext());
-        } catch (const std::exception& e) {
-            LOG_ERROR(packet6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
-        }
-
-        // Handle next signal received by the process. It must be called after
-        // an attempt to receive a packet to properly handle server shut down.
-        // The SIGTERM or SIGINT will be received prior to, or during execution
-        // of select() (select is invoked by receivePacket()). When that happens,
-        // select will be interrupted. The signal handler will be invoked
-        // immediately after select(). The handler will set the shutdown flag
-        // and cause the process to terminate before the next select() function
-        // is called. If the function was called before receivePacket the
-        // process could wait up to the duration of timeout of select() to
-        // terminate.
-        try {
-            handleSignal();
+            run_one();
         } catch (const std::exception& e) {
-            // An (a standard or ISC) exception occurred.
-            LOG_ERROR(dhcp6_logger, DHCP6_HANDLE_SIGNAL_EXCEPTION)
+            // General catch-all standard exceptions that are not caught by more
+            // specific catches.
+            LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_STD_EXCEPTION)
                 .arg(e.what());
+        } catch (...) {
+            // General catch-all non-standard exception that are not caught
+            // by more specific catches.
+            LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION);
         }
+    }
 
-        // Timeout may be reached or signal received, which breaks select()
-        // with no packet received
-        if (!query) {
-            continue;
-        }
+    return (true);
+}
 
-        // In order to parse the DHCP options, the server needs to use some
-        // configuration information such as: existing option spaces, option
-        // definitions etc. This is the kind of information which is not
-        // available in the libdhcp, so we need to supply our own implementation
-        // of the option parsing function here, which would rely on the
-        // configuration data.
-        query->setCallback(boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
-                                       _3, _4, _5));
+void Dhcpv6Srv::run_one() {
+    // client's message and server's response
+    Pkt6Ptr query;
+    Pkt6Ptr rsp;
 
-        bool skip_unpack = false;
+    try {
+        uint32_t timeout = 1000;
+        LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT).arg(timeout);
+        query = receivePacket(timeout);
+
+        // Log if packet has arrived. We can't log the detailed information
+        // about the DHCP message because it hasn't been unpacked/parsed
+        // yet, and it can't be parsed at this point because hooks will
+        // have to process it first. The only information available at this
+        // point are: the interface, source address and destination addresses
+        // and ports.
+        if (query) {
+            LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC, DHCP6_BUFFER_RECEIVED)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getRemotePort())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getLocalPort())
+                .arg(query->getIface());
 
-        // The packet has just been received so contains the uninterpreted wire
-        // data; execute callouts registered for buffer6_receive.
-        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
+            // Log reception of the packet. We need to increase it early, as
+            // any failures in unpacking will cause the packet to be dropped.
+            // we will increase type specific packets further down the road.
+            // See processStatsReceived().
+            StatsMgr::instance().addValue("pkt6-received", static_cast<int64_t>(1));
+
+        } else {
+            LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT_INTERRUPTED)
+                .arg(timeout);
+        }
+
+    } catch (const SignalInterruptOnSelect) {
+        // Packet reception interrupted because a signal has been received.
+        // This is not an error because we might have received a SIGTERM,
+        // SIGINT or SIGHUP which are handled by the server. For signals
+        // that are not handled by the server we rely on the default
+        // behavior of the system.
+        LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT_SIGNAL)
+            .arg(signal_set_->getNext());
+    } catch (const std::exception& e) {
+        LOG_ERROR(packet6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
+    }
+
+    // Handle next signal received by the process. It must be called after
+    // an attempt to receive a packet to properly handle server shut down.
+    // The SIGTERM or SIGINT will be received prior to, or during execution
+    // of select() (select is invoked by receivePacket()). When that happens,
+    // select will be interrupted. The signal handler will be invoked
+    // immediately after select(). The handler will set the shutdown flag
+    // and cause the process to terminate before the next select() function
+    // is called. If the function was called before receivePacket the
+    // process could wait up to the duration of timeout of select() to
+    // terminate.
+    try {
+        handleSignal();
+    } catch (const std::exception& e) {
+        // An (a standard or ISC) exception occurred.
+        LOG_ERROR(dhcp6_logger, DHCP6_HANDLE_SIGNAL_EXCEPTION)
+            .arg(e.what());
+    }
+
+    // Timeout may be reached or signal received, which breaks select()
+    // with no packet received
+    if (!query) {
+        return;
+    }
+
+    processPacket(query, rsp);
+
+    if (!rsp) {
+        return;
+    }
+
+    try {
+
+        // Now all fields and options are constructed into output wire buffer.
+        // Option objects modification does not make sense anymore. Hooks
+        // can only manipulate wire buffer at this stage.
+        // Let's execute all callouts registered for buffer6_send
+        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
             // Delete previously set arguments
             callout_handle->deleteAllArguments();
 
             // Pass incoming packet as argument
-            callout_handle->setArgument("query6", query);
+            callout_handle->setArgument("response6", rsp);
 
             // Call callouts
-            HooksManager::callCallouts(Hooks.hook_index_buffer6_receive_, *callout_handle);
+            HooksManager::callCallouts(Hooks.hook_index_buffer6_send_, *callout_handle);
 
             // Callouts decided to skip the next processing step. The next
             // processing step would to parse the packet, so skip at this
-            // stage means that callouts did the parsing already, so server
-            // should skip parsing.
+            // stage means drop.
             if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_BUFFER_RCVD_SKIP)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getIface());
-                skip_unpack = true;
+                LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP)
+                    .arg(rsp->getLabel());
+                return;
             }
 
-            /// @todo: Add support for DROP status.
-
-            callout_handle->getArgument("query6", query);
-        }
-
-        // Unpack the packet information unless the buffer6_receive callouts
-        // indicated they did it
-        if (!skip_unpack) {
-            try {
-                LOG_DEBUG(options6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_UNPACK)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getIface());
-                query->unpack();
-            } catch (const std::exception &e) {
-                // Failed to parse the packet.
-                LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_DETAIL,
-                          DHCP6_PACKET_DROP_PARSE_FAIL)
-                    .arg(query->getRemoteAddr().toText())
-                    .arg(query->getLocalAddr().toText())
-                    .arg(query->getIface())
-                    .arg(e.what());
-
-                // Increase the statistics of parse failures and dropped packets.
-                StatsMgr::instance().addValue("pkt6-parse-failed",
-                                              static_cast<int64_t>(1));
-                StatsMgr::instance().addValue("pkt6-receive-drop",
-                                              static_cast<int64_t>(1));
-                continue;
-            }
+            /// @todo: Add support for DROP status
+
+            callout_handle->getArgument("response6", rsp);
         }
 
-        // Update statistics accordingly for received packet.
-        processStatsReceived(query);
+        LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_RESPONSE_DATA)
+            .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
 
-        // Check if received query carries server identifier matching
-        // server identifier being used by the server.
-        if (!testServerID(query)) {
+        sendPacket(rsp);
 
-            // Increase the statistic of dropped packets.
-            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
-            continue;
-        }
+        // Update statistics accordingly for sent packet.
+        processStatsSent(rsp);
 
-        // Check if the received query has been sent to unicast or multicast.
-        // The Solicit, Confirm, Rebind and Information Request will be
-        // discarded if sent to unicast address.
-        if (!testUnicast(query)) {
+    } catch (const std::exception& e) {
+        LOG_ERROR(packet6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
+    }
+}
 
-            // Increase the statistic of dropped packets.
-            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
-            continue;
-        }
+void
+Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
+    // In order to parse the DHCP options, the server needs to use some
+    // configuration information such as: existing option spaces, option
+    // definitions etc. This is the kind of information which is not
+    // available in the libdhcp, so we need to supply our own implementation
+    // of the option parsing function here, which would rely on the
+    // configuration data.
+    query->setCallback(boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
+                                   _3, _4, _5));
+
+    bool skip_unpack = false;
+
+    // The packet has just been received so contains the uninterpreted wire
+    // data; execute callouts registered for buffer6_receive.
+    if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_receive_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
-        LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC_DATA, DHCP6_PACKET_RECEIVED)
-            .arg(query->getLabel())
-            .arg(query->getName())
-            .arg(static_cast<int>(query->getType()))
-            .arg(query->getRemoteAddr())
-            .arg(query->getLocalAddr())
-            .arg(query->getIface());
-        LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
-            .arg(query->getLabel())
-            .arg(query->toText());
+        // Delete previously set arguments
+        callout_handle->deleteAllArguments();
 
-        // At this point the information in the packet has been unpacked into
-        // the various packet fields and option objects has been created.
-        // Execute callouts registered for packet6_receive.
-        if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
-            CalloutHandlePtr callout_handle = getCalloutHandle(query);
+        // Pass incoming packet as argument
+        callout_handle->setArgument("query6", query);
 
-            // Delete previously set arguments
-            callout_handle->deleteAllArguments();
+        // Call callouts
+        HooksManager::callCallouts(Hooks.hook_index_buffer6_receive_, *callout_handle);
 
-            // Pass incoming packet as argument
-            callout_handle->setArgument("query6", query);
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to parse the packet, so skip at this
+        // stage means that callouts did the parsing already, so server
+        // should skip parsing.
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_BUFFER_RCVD_SKIP)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getIface());
+            skip_unpack = true;
+        }
 
-            // Call callouts
-            HooksManager::callCallouts(Hooks.hook_index_pkt6_receive_, *callout_handle);
+        /// @todo: Add support for DROP status.
 
-            // Callouts decided to skip the next processing step. The next
-            // processing step would to process the packet, so skip at this
-            // stage means drop.
-            if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_RCVD_SKIP)
-                    .arg(query->getLabel());
-                continue;
-            }
+        callout_handle->getArgument("query6", query);
+    }
 
-            /// @todo: Add support for DROP status.
+    // Unpack the packet information unless the buffer6_receive callouts
+    // indicated they did it
+    if (!skip_unpack) {
+        try {
+            LOG_DEBUG(options6_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_UNPACK)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getIface());
+            query->unpack();
+        } catch (const std::exception &e) {
+            // Failed to parse the packet.
+            LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_DETAIL,
+                      DHCP6_PACKET_DROP_PARSE_FAIL)
+                .arg(query->getRemoteAddr().toText())
+                .arg(query->getLocalAddr().toText())
+                .arg(query->getIface())
+                .arg(e.what());
 
-            callout_handle->getArgument("query6", query);
+            // Increase the statistics of parse failures and dropped packets.
+            StatsMgr::instance().addValue("pkt6-parse-failed",
+                                          static_cast<int64_t>(1));
+            StatsMgr::instance().addValue("pkt6-receive-drop",
+                                          static_cast<int64_t>(1));
+            return;
         }
+    }
 
-        // Assign this packet to a class, if possible
-        classifyPacket(query);
+    // Update statistics accordingly for received packet.
+    processStatsReceived(query);
 
-        try {
-            NameChangeRequestPtr ncr;
+    // Check if received query carries server identifier matching
+    // server identifier being used by the server.
+    if (!testServerID(query)) {
 
-            switch (query->getType()) {
-            case DHCPV6_SOLICIT:
-                rsp = processSolicit(query);
-                    break;
+        // Increase the statistic of dropped packets.
+        StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
+        return;
+    }
 
-            case DHCPV6_REQUEST:
-                rsp = processRequest(query);
-                break;
+    // Check if the received query has been sent to unicast or multicast.
+    // The Solicit, Confirm, Rebind and Information Request will be
+    // discarded if sent to unicast address.
+    if (!testUnicast(query)) {
 
-            case DHCPV6_RENEW:
-                rsp = processRenew(query);
-                break;
+        // Increase the statistic of dropped packets.
+        StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
+        return;
+    }
 
-            case DHCPV6_REBIND:
-                rsp = processRebind(query);
-                break;
+    LOG_DEBUG(packet6_logger, DBG_DHCP6_BASIC_DATA, DHCP6_PACKET_RECEIVED)
+        .arg(query->getLabel())
+        .arg(query->getName())
+        .arg(static_cast<int>(query->getType()))
+        .arg(query->getRemoteAddr())
+        .arg(query->getLocalAddr())
+        .arg(query->getIface());
+    LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
+        .arg(query->getLabel())
+        .arg(query->toText());
 
-            case DHCPV6_CONFIRM:
-                rsp = processConfirm(query);
-                break;
+    // At this point the information in the packet has been unpacked into
+    // the various packet fields and option objects has been created.
+    // Execute callouts registered for packet6_receive.
+    if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_receive_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
-            case DHCPV6_RELEASE:
-                rsp = processRelease(query);
-                break;
+        // Delete previously set arguments
+        callout_handle->deleteAllArguments();
 
-            case DHCPV6_DECLINE:
-                rsp = processDecline(query);
-                break;
+        // Pass incoming packet as argument
+        callout_handle->setArgument("query6", query);
 
-            case DHCPV6_INFORMATION_REQUEST:
-                rsp = processInfRequest(query);
-                break;
+        // Call callouts
+        HooksManager::callCallouts(Hooks.hook_index_pkt6_receive_, *callout_handle);
 
-            default:
-                // We received a packet type that we do not recognize.
-                LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_UNKNOWN_MSG_RECEIVED)
-                    .arg(static_cast<int>(query->getType()))
-                    .arg(query->getIface());
-                // Only action is to output a message if debug is enabled,
-                // and that will be covered by the debug statement before
-                // the "switch" statement.
-                ;
-            }
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to process the packet, so skip at this
+        // stage means drop.
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_RCVD_SKIP)
+                .arg(query->getLabel());
+            return;
+        }
 
-        } catch (const RFCViolation& e) {
-            LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
-                .arg(query->getName())
-                .arg(query->getRemoteAddr().toText())
-                .arg(e.what());
+        /// @todo: Add support for DROP status.
 
-            // Increase the statistic of dropped packets.
-            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
+        callout_handle->getArgument("query6", query);
+    }
 
-        } catch (const std::exception& e) {
+    // Assign this packet to a class, if possible
+    classifyPacket(query);
 
-            // Catch-all exception (at least for ones based on the isc Exception
-            // class, which covers more or less all that are explicitly raised
-            // in the Kea code), but also the standard one, which may possibly be
-            // thrown from boost code.  Just log the problem and ignore the packet.
-            // (The problem is logged as a debug message because debug is
-            // disabled by default - it prevents a DDOS attack based on the
-            // sending of problem packets.)
-            LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
-                .arg(query->getName())
-                .arg(query->getRemoteAddr().toText())
-                .arg(e.what());
+    try {
+        NameChangeRequestPtr ncr;
 
-            // Increase the statistic of dropped packets.
-            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
-        }
+        switch (query->getType()) {
+        case DHCPV6_SOLICIT:
+            rsp = processSolicit(query);
+            break;
 
-        if (rsp) {
+        case DHCPV6_REQUEST:
+            rsp = processRequest(query);
+            break;
 
-            // Process relay-supplied options. It is important to call this very
-            // late in the process, because we now have all the options the
-            // server wanted to send already set. This is important, because
-            // RFC6422, section 6 states:
-            //
-            //   The server SHOULD discard any options that appear in the RSOO
-            //   for which it already has one or more candidates.
-            //
-            // So we ignore any RSOO options if there's an option with the same
-            // code already present.
-            processRSOO(query, rsp);
+        case DHCPV6_RENEW:
+            rsp = processRenew(query);
+            break;
 
-            rsp->setRemoteAddr(query->getRemoteAddr());
-            rsp->setLocalAddr(query->getLocalAddr());
+        case DHCPV6_REBIND:
+            rsp = processRebind(query);
+            break;
 
-            if (rsp->relay_info_.empty()) {
-                // Direct traffic, send back to the client directly
-                rsp->setRemotePort(DHCP6_CLIENT_PORT);
-            } else {
-                // Relayed traffic, send back to the relay agent
-                rsp->setRemotePort(DHCP6_SERVER_PORT);
-            }
+        case DHCPV6_CONFIRM:
+            rsp = processConfirm(query);
+            break;
 
-            rsp->setLocalPort(DHCP6_SERVER_PORT);
-            rsp->setIndex(query->getIndex());
-            rsp->setIface(query->getIface());
-
-            // Specifies if server should do the packing
-            bool skip_pack = false;
-
-            // Server's reply packet now has all options and fields set.
-            // Options are represented by individual objects, but the
-            // output wire data has not been prepared yet.
-            // Execute all callouts registered for packet6_send
-            if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
-                CalloutHandlePtr callout_handle = getCalloutHandle(query);
-
-                // Delete all previous arguments
-                callout_handle->deleteAllArguments();
-
-                // Set our response
-                callout_handle->setArgument("response6", rsp);
-
-                // Call all installed callouts
-                HooksManager::callCallouts(Hooks.hook_index_pkt6_send_, *callout_handle);
-
-                // Callouts decided to skip the next processing step. The next
-                // processing step would to pack the packet (create wire data).
-                // That step will be skipped if any callout sets skip flag.
-                // It essentially means that the callout already did packing,
-                // so the server does not have to do it again.
-                if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                    LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_SEND_SKIP)
-                        .arg(rsp->getLabel());
-                    skip_pack = true;
-                }
+        case DHCPV6_RELEASE:
+            rsp = processRelease(query);
+            break;
 
-                /// @todo: Add support for DROP status
-            }
+        case DHCPV6_DECLINE:
+            rsp = processDecline(query);
+            break;
 
-            if (!skip_pack) {
-                try {
-                    rsp->pack();
-                } catch (const std::exception& e) {
-                    LOG_ERROR(options6_logger, DHCP6_PACK_FAIL)
-                        .arg(e.what());
-                    continue;
-                }
+        case DHCPV6_INFORMATION_REQUEST:
+            rsp = processInfRequest(query);
+            break;
 
-            }
+        default:
+            // We received a packet type that we do not recognize.
+            LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_UNKNOWN_MSG_RECEIVED)
+                .arg(static_cast<int>(query->getType()))
+                .arg(query->getIface());
+            // Only action is to output a message if debug is enabled,
+            // and that will be covered by the debug statement before
+            // the "switch" statement.
+            ;
+        }
 
-            try {
+    } catch (const RFCViolation& e) {
+        LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
+            .arg(query->getName())
+            .arg(query->getRemoteAddr().toText())
+            .arg(e.what());
+
+        // Increase the statistic of dropped packets.
+        StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
+
+    } catch (const std::exception& e) {
+
+        // Catch-all exception (at least for ones based on the isc Exception
+        // class, which covers more or less all that are explicitly raised
+        // in the Kea code), but also the standard one, which may possibly be
+        // thrown from boost code.  Just log the problem and ignore the packet.
+        // (The problem is logged as a debug message because debug is
+        // disabled by default - it prevents a DDOS attack based on the
+        // sending of problem packets.)
+        LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
+            .arg(query->getName())
+            .arg(query->getRemoteAddr().toText())
+            .arg(e.what());
+
+        // Increase the statistic of dropped packets.
+        StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
+    }
 
-                // Now all fields and options are constructed into output wire buffer.
-                // Option objects modification does not make sense anymore. Hooks
-                // can only manipulate wire buffer at this stage.
-                // Let's execute all callouts registered for buffer6_send
-                if (HooksManager::calloutsPresent(Hooks.hook_index_buffer6_send_)) {
-                    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+    if (!rsp) {
+        return;
+    }
 
-                    // Delete previously set arguments
-                    callout_handle->deleteAllArguments();
+    // Process relay-supplied options. It is important to call this very
+    // late in the process, because we now have all the options the
+    // server wanted to send already set. This is important, because
+    // RFC6422, section 6 states:
+    //
+    //   The server SHOULD discard any options that appear in the RSOO
+    //   for which it already has one or more candidates.
+    //
+    // So we ignore any RSOO options if there's an option with the same
+    // code already present.
+    processRSOO(query, rsp);
 
-                    // Pass incoming packet as argument
-                    callout_handle->setArgument("response6", rsp);
+    rsp->setRemoteAddr(query->getRemoteAddr());
+    rsp->setLocalAddr(query->getLocalAddr());
 
-                    // Call callouts
-                    HooksManager::callCallouts(Hooks.hook_index_buffer6_send_, *callout_handle);
+    if (rsp->relay_info_.empty()) {
+        // Direct traffic, send back to the client directly
+        rsp->setRemotePort(DHCP6_CLIENT_PORT);
+    } else {
+        // Relayed traffic, send back to the relay agent
+        rsp->setRemotePort(DHCP6_SERVER_PORT);
+    }
 
-                    // Callouts decided to skip the next processing step. The next
-                    // processing step would to parse the packet, so skip at this
-                    // stage means drop.
-                    if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
-                        LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP)
-                            .arg(rsp->getLabel());
-                        continue;
-                    }
+    rsp->setLocalPort(DHCP6_SERVER_PORT);
+    rsp->setIndex(query->getIndex());
+    rsp->setIface(query->getIface());
 
-                    /// @todo: Add support for DROP status
+    // Specifies if server should do the packing
+    bool skip_pack = false;
 
-                    callout_handle->getArgument("response6", rsp);
-                }
+    // Server's reply packet now has all options and fields set.
+    // Options are represented by individual objects, but the
+    // output wire data has not been prepared yet.
+    // Execute all callouts registered for packet6_send
+    if (HooksManager::calloutsPresent(Hooks.hook_index_pkt6_send_)) {
+        CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
-                LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL_DATA,
-                          DHCP6_RESPONSE_DATA)
-                    .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
+        // Delete all previous arguments
+        callout_handle->deleteAllArguments();
 
-                sendPacket(rsp);
+        // Set our response
+        callout_handle->setArgument("response6", rsp);
 
-                // Update statistics accordingly for sent packet.
-                processStatsSent(rsp);
+        // Call all installed callouts
+        HooksManager::callCallouts(Hooks.hook_index_pkt6_send_, *callout_handle);
 
-            } catch (const std::exception& e) {
-                LOG_ERROR(packet6_logger, DHCP6_PACKET_SEND_FAIL)
-                    .arg(e.what());
-            }
+        // Callouts decided to skip the next processing step. The next
+        // processing step would to pack the packet (create wire data).
+        // That step will be skipped if any callout sets skip flag.
+        // It essentially means that the callout already did packing,
+        // so the server does not have to do it again.
+        if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
+            LOG_DEBUG(hooks_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_PACKET_SEND_SKIP)
+                .arg(rsp->getLabel());
+            skip_pack = true;
         }
 
+        /// @todo: Add support for DROP status
+    }
+
+    if (!skip_pack) {
+        try {
+            rsp->pack();
         } catch (const std::exception& e) {
-            // General catch-all standard exceptions that are not caught by more
-            // specific catches.
-            LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION)
-                .arg(e.what());
-        } catch (...) {
-            // General catch-all non-standard exception that are not caught
-            // by more specific catches.
-            LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION)
-                .arg("an unknown exception not derived from std::exception");
+            LOG_ERROR(options6_logger, DHCP6_PACK_FAIL).arg(e.what());
+            return;
         }
-    }
 
-    return (true);
+    }
 }
 
 std::string

+ 20 - 6
src/bin/dhcp6/dhcp6_srv.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 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
@@ -89,14 +89,28 @@ public:
 
     /// @brief Main server processing loop.
     ///
-    /// Main server processing loop. Receives incoming packets, verifies
-    /// their correctness, generates appropriate answer (if needed) and
-    /// transmits responses.
+    /// Main server processing loop. Call the processing step routine
+    /// until shut down.
     ///
-    /// @return true, if being shut down gracefully, fail if experienced
-    ///         critical error.
+    /// @return true, if being shut down gracefully, never fail.
     bool run();
 
+    /// @brief Main server processing step.
+    ///
+    /// Main server processing step. Receives one incoming packet, calls
+    /// the processing packet routing and (if necessary) transmits
+    /// a response.
+    void run_one();
+
+    /// @brief Process a single incoming DHCPv6 packet.
+    ///
+    /// It verifies correctness of the passed packet, call per-type processXXX
+    /// methods, generates appropriate answer.
+    ///
+    /// @param query A pointer to the packet to be processed.
+    /// @param rsp A pointer to the response
+    void processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp);
+
     /// @brief Instructs the server to shut down.
     void shutdown();