libdhcp6_la_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
libdhcp6_la_SOURCES += json_config_parser.cc json_config_parser.h
libdhcp6_la_SOURCES += dhcp6to4_ipc.cc dhcp6to4_ipc.h
+libdhcp6_la_SOURCES += client_handler.cc client_handler.h
libdhcp6_la_SOURCES += dhcp6_lexer.ll location.hh position.hh stack.hh
libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h
libdhcp6_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
--- /dev/null
+// Copyright (C) 2020 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <dhcp6/client_handler.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+mutex ClientHandler::mutex_;
+
+ClientHandler::ClientContainer ClientHandler::clients_;
+
+ClientHandler::ClientHandler() : locked_() {
+}
+
+ClientHandler::~ClientHandler() {
+ if (locked_) {
+ lock_guard<mutex> lock_(mutex_);
+ unLock();
+ }
+ locked_.reset();
+}
+
+ClientHandler::Client::Client(Pkt6Ptr query)
+ : query_(query), thread_(this_thread::get_id()) {
+ if (!query) {
+ isc_throw(InvalidParameter, "null query in ClientHandler");
+ }
+ if (!query->getClientId()) {
+ isc_throw(InvalidParameter, "query has no client Id in ClientHandler");
+ }
+}
+
+Pkt6Ptr
+ClientHandler::lookup(const DuidPtr& duid) {
+ if (!duid) {
+ isc_throw(InvalidParameter, "duid is null in ClientHandler::lookup");
+ }
+ auto it = clients_.find(duid->getDuid());
+ if (it == clients_.end()) {
+ return (Pkt6Ptr());
+ }
+ return (it->query_);
+}
+
+void
+ClientHandler::lock() {
+ if (!locked_) {
+ isc_throw(Unexpected, "nothing to lock in ClientHandler::lock");
+ }
+ Client client(locked_);
+ clients_.insert(Client(locked_));
+}
+
+void
+ClientHandler::unLock() {
+ if (!locked_) {
+ isc_throw(Unexpected, "nothing to unlock in ClientHandler::unLock");
+ }
+ const DuidPtr& duid = locked_->getClientId();
+ if (!duid) {
+ isc_throw(Unexpected, "no duid unlock in ClientHandler::unLock");
+ }
+ auto it = clients_.find(duid->getDuid());
+ if (it == clients_.end()) {
+ // Should not happen
+ return;
+ }
+ clients_.erase(it);
+}
+
+bool
+ClientHandler::tryLock(Pkt6Ptr query) {
+ if (!query) {
+ isc_throw(InvalidParameter, "null query in ClientHandler::tryLock");
+ }
+ if (locked_) {
+ isc_throw(Unexpected, "already handling in ClientHandler::tryLock");
+ }
+ const DuidPtr& duid = query->getClientId();
+ if (!duid) {
+ // Can't do something useful: cross fingers.
+ return (false);
+ }
+ if (duid->getDuid().empty()) {
+ // A lot of code assumes this will never happen...
+ isc_throw(Unexpected, "empty DUID in ClientHandler::tryLock");
+ }
+ lock_guard<mutex> lock_(mutex_);
+ const Pkt6Ptr& duplicate = lookup(duid);
+ if (duplicate) {
+ // Should log.
+ return (true);
+ }
+ locked_ = query;
+ lock();
+ return (false);
+}
+
+} // namespace dhcp
+} // namespace isc
--- /dev/null
+// Copyright (C) 2020 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef CLIENT_HANDLER_H
+#define CLIENT_HANDLER_H
+
+#include <dhcp/pkt6.h>
+#include <boost/noncopyable.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <mutex>
+#include <thread>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Client race avoidance RAII handler.
+class ClientHandler : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ClientHandler();
+
+ /// @brief Destructor.
+ ///
+ /// Releases the client if it was acquired.
+ virtual ~ClientHandler();
+
+ /// @brief Tries to acquires a client.
+ ///
+ /// @param query The query from the client.
+ /// @return true if the client was acquired, false if there is already
+ /// a query from the same client.
+ bool tryLock(Pkt6Ptr query);
+
+private:
+
+ /// @brief Structure representing a client.
+ struct Client {
+
+ /// @brief Constructor.
+ ///
+ /// @param query The query.
+ /// @throw if the query is null or has empty client ID.
+ Client(Pkt6Ptr query);
+
+ /// @brief The query being processed.
+ Pkt6Ptr query_;
+
+ /// @brief The ID of the thread processing the query.
+ std::thread::id thread_;
+
+ /// @brief Key extractor.
+ ///
+ /// Returns the content of the Duid aka client ID.
+ const std::vector<uint8_t>& getClientId() const {
+ return (query_->getClientId()->getDuid());
+ }
+ };
+
+ /// @brief Query locked by this handler.
+ Pkt6Ptr locked_;
+
+ /// @brief Mutex to protect the client container.
+ static std::mutex mutex_;
+
+ /// @brief Lookup a client.
+ ///
+ /// The mutex must be held by the caller.
+ ///
+ /// @param duid The duid of the query from the client.
+ /// @return The query holding the client or null.
+ static Pkt6Ptr lookup(const DuidPtr& duid);
+
+ /// @brief Acquire a client.
+ ///
+ /// The mutex must be held by the caller.
+ void lock();
+
+ /// @brief Release a client.
+ ///
+ /// The mutex must be held by the caller.
+ void unLock();
+
+ /// @brief The type of the client container.
+ typedef boost::multi_index_container<
+
+ // This container stores clients.
+ Client,
+
+ // Start specification of indexes here.
+ boost::multi_index::indexed_by<
+
+ // First index is used to search by Duid.
+ boost::multi_index::hashed_unique<
+
+ // Duid content is extracted by calling Client::getClientId()
+ boost::multi_index::const_mem_fun<
+ Client, const std::vector<uint8_t>&, &Client::getClientId
+ >
+ >
+ >
+ > ClientContainer;
+
+ /// @brief The client container.
+ static ClientContainer clients_;
+};
+
+} // namespace isc
+} // namespace dhcp
+
+#endif // CLIENT_HANDLER_H
#include <dhcp/option_vendor_class.h>
#include <dhcp/option_int_array.h>
#include <dhcp/pkt6.h>
+#include <dhcp6/client_handler.h>
#include <dhcp6/dhcp6to4_ipc.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/dhcp6_srv.h>
return;
}
+ // Create a client race avoidance RAII handler.
+ ClientHandler client_handler;
+ bool drop = false;
+
+ // Check for lease modifier queries from the same client being processed.
+ if (MultiThreadingMgr::instance().getMode() &&
+ ((query->getType() == DHCPV6_SOLICIT) ||
+ (query->getType() == DHCPV6_REQUEST) ||
+ (query->getType() == DHCPV6_RENEW) ||
+ (query->getType() == DHCPV6_REBIND) ||
+ (query->getType() == DHCPV6_RELEASE) ||
+ (query->getType() == DHCPV6_DECLINE))) {
+ drop = client_handler.tryLock(query);
+ }
+
+ // Stop here if ClientHandler tryLock decided the packet is a duplicate.
+ if (drop) {
+ return;
+ }
+
// Let's create a simplified client context here.
AllocEngine::ClientContext6 ctx;
- bool drop = false;
initContext(query, ctx, drop);
// Stop here if initContext decided to drop the packet.