Parcourir la source

[master] Merged trac4267 (final run_one())

Francis Dupont il y a 9 ans
Parent
commit
2047a44ad5

+ 1 - 0
AUTHORS

@@ -89,6 +89,7 @@ We have received the following contributions:
 
 
  - Jinmei Tatuya
  - Jinmei Tatuya
    2015-10: Pkt4o6 class improvements
    2015-10: Pkt4o6 class improvements
+   2015-11: split Dhcpv4Srv::run() into run() and processPacket()
 
 
  - Sebastien Couture, Ubity Inc
  - Sebastien Couture, Ubity Inc
    2015-12: Fixes to MySQL schema creation
    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.
 has failed. The first argument identifies the client and the DHCP transaction.
 The second argument includes the error string.
 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
 % 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
 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
 // 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
 // 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
 bool
 Dhcpv4Srv::run() {
 Dhcpv4Srv::run() {
     while (!shutdown_) {
     while (!shutdown_) {
-        // client's message and server's response
-        Pkt4Ptr query;
-        Pkt4Ptr rsp;
-
         try {
         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);
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
 
             // Delete previously set arguments
             // Delete previously set arguments
             callout_handle->deleteAllArguments();
             callout_handle->deleteAllArguments();
 
 
             // Pass incoming packet as argument
             // Pass incoming packet as argument
-            callout_handle->setArgument("query4", query);
+            callout_handle->setArgument("response4", rsp);
 
 
             // Call callouts
             // Call callouts
-            HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
+            HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
                                        *callout_handle);
                                        *callout_handle);
 
 
             // Callouts decided to skip the next processing step. The next
             // Callouts decided to skip the next processing step. The next
             // processing step would to parse the packet, so skip at this
             // 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) {
             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 {
         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 (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());
                 .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",
             isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
                                                       static_cast<int64_t>(1));
                                                       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) {
         } 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());
                 .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
 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
 // 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
 // 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.
     /// @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();
     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.
     /// @brief Instructs the server to shut down.
     void shutdown();
     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
 specifies the client and transaction identification information, the
 second argument specifies packet type.
 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
 % 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
 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
 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.
 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
 % 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
 A debug message noting that the server has received the specified type of
 packet on the specified interface. The first argument specifies the
 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
 // 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
 // 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() {
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
     while (!shutdown_) {
-        // client's message and server's response
-        Pkt6Ptr query;
-        Pkt6Ptr rsp;
-
-        try {
-
         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) {
         } 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());
                 .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);
             CalloutHandlePtr callout_handle = getCalloutHandle(query);
 
 
             // Delete previously set arguments
             // Delete previously set arguments
             callout_handle->deleteAllArguments();
             callout_handle->deleteAllArguments();
 
 
             // Pass incoming packet as argument
             // Pass incoming packet as argument
-            callout_handle->setArgument("query6", query);
+            callout_handle->setArgument("response6", rsp);
 
 
             // Call callouts
             // 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
             // Callouts decided to skip the next processing step. The next
             // processing step would to parse the packet, so skip at this
             // 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) {
             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) {
         } 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
 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
 // 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
 // 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.
     /// @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();
     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.
     /// @brief Instructs the server to shut down.
     void shutdown();
     void shutdown();