mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-02 06:55:16 +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 += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
|
||||||
libdhcp6_la_SOURCES += json_config_parser.cc json_config_parser.h
|
libdhcp6_la_SOURCES += json_config_parser.cc json_config_parser.h
|
||||||
libdhcp6_la_SOURCES += dhcp6to4_ipc.cc dhcp6to4_ipc.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_lexer.ll location.hh position.hh stack.hh
|
||||||
libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h
|
libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h
|
||||||
libdhcp6_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.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_vendor_class.h>
|
||||||
#include <dhcp/option_int_array.h>
|
#include <dhcp/option_int_array.h>
|
||||||
#include <dhcp/pkt6.h>
|
#include <dhcp/pkt6.h>
|
||||||
|
#include <dhcp6/client_handler.h>
|
||||||
#include <dhcp6/dhcp6to4_ipc.h>
|
#include <dhcp6/dhcp6to4_ipc.h>
|
||||||
#include <dhcp6/dhcp6_log.h>
|
#include <dhcp6/dhcp6_log.h>
|
||||||
#include <dhcp6/dhcp6_srv.h>
|
#include <dhcp6/dhcp6_srv.h>
|
||||||
@@ -815,9 +816,28 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
|
|||||||
return;
|
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.
|
// Let's create a simplified client context here.
|
||||||
AllocEngine::ClientContext6 ctx;
|
AllocEngine::ClientContext6 ctx;
|
||||||
bool drop = false;
|
|
||||||
initContext(query, ctx, drop);
|
initContext(query, ctx, drop);
|
||||||
|
|
||||||
// Stop here if initContext decided to drop the packet.
|
// Stop here if initContext decided to drop the packet.
|
||||||
|
Reference in New Issue
Block a user