mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
[#1147] Checkpoint: began v6 client
This commit is contained in:
@@ -34,6 +34,7 @@ libdhcp6_la_SOURCES += dhcp6_srv.cc dhcp6_srv.h
|
||||
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
|
||||
|
109
src/bin/dhcp6/client_handler.cc
Normal file
109
src/bin/dhcp6/client_handler.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
// 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
|
117
src/bin/dhcp6/client_handler.h
Normal file
117
src/bin/dhcp6/client_handler.h
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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
|
@@ -28,6 +28,7 @@
|
||||
#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>
|
||||
@@ -815,9 +816,28 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
|
||||
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.
|
||||
|
Reference in New Issue
Block a user