2
0
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:
Francis Dupont
2020-05-05 18:21:52 +02:00
parent 999ed6097e
commit 3e05a2fa4c
4 changed files with 248 additions and 1 deletions

View File

@@ -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

View 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

View 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

View File

@@ -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.