// Copyright (C) 2020-2023 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 #include #include #include #include #include using namespace std; using namespace isc::util; using namespace isc::log; namespace isc { namespace dhcp { ClientHandler::Client::Client(Pkt6Ptr query, DuidPtr client_id) : query_(query), thread_(this_thread::get_id()) { // Sanity checks. if (!query) { isc_throw(InvalidParameter, "null query in ClientHandler"); } if (!client_id) { isc_throw(InvalidParameter, "null client-id in ClientHandler"); } duid_ = client_id->getDuid(); } mutex ClientHandler::mutex_; ClientHandler::ClientContainer ClientHandler::clients_; ClientHandler::ClientPtr ClientHandler::lookup(const DuidPtr& duid) { // Sanity check. if (!duid) { isc_throw(InvalidParameter, "null duid in ClientHandler::lookup"); } auto it = clients_.find(duid->getDuid()); if (it == clients_.end()) { return (ClientPtr()); } return (*it); } void ClientHandler::add(const ClientPtr& client) { // Sanity check. if (!client) { isc_throw(InvalidParameter, "null client in ClientHandler::add"); } // Assume insert will never fail so not checking its result. clients_.insert(client); } void ClientHandler::del(const DuidPtr& duid) { // Sanity check. if (!duid) { isc_throw(InvalidParameter, "null duid in ClientHandler::del"); } // Assume erase will never fail so not checking its result. clients_.erase(duid->getDuid()); } ClientHandler::ClientHandler() : client_(), locked_() { } ClientHandler::~ClientHandler() { if (locked_) { lock_guard lk(mutex_); unLock(); } } bool ClientHandler::tryLock(Pkt6Ptr query, ContinuationPtr cont) { // Sanity checks. 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 (true); } if (duid->getDuid().empty()) { // A lot of code assumes this will never happen... isc_throw(Unexpected, "empty DUID in ClientHandler::tryLock"); } ClientPtr holder; Pkt6Ptr next_query; client_.reset(new Client(query, duid)); { // Try to acquire the lock and return the holder when it failed. lock_guard lk(mutex_); holder = lookup(duid); if (!holder) { locked_ = duid; lock(); return (true); } // This query can be a duplicate so put the continuation. if (cont) { next_query = holder->next_query_; holder->next_query_ = query; holder->cont_ = cont; } } if (cont) { if (next_query) { // Logging a warning as it is supposed to be a rare event // with well behaving clients... LOG_DEBUG(bad_packet6_logger, DBGLVL_PKT_HANDLING, DHCP6_PACKET_DROP_DUPLICATE) .arg(next_query->makeLabel(next_query->getClientId(), nullptr)) .arg(next_query->toText()) .arg(this_thread::get_id()) .arg(holder->query_->makeLabel(holder->query_->getClientId(), nullptr)) .arg(holder->query_->toText()) .arg(holder->thread_); stats::StatsMgr::instance().addValue("pkt6-receive-drop", static_cast(1)); } } else { // Logging a warning as it is supposed to be a rare event // with well behaving clients... LOG_DEBUG(bad_packet6_logger, DBGLVL_PKT_HANDLING, DHCP6_PACKET_DROP_DUPLICATE) .arg(query->makeLabel(query->getClientId(), nullptr)) .arg(query->toText()) .arg(this_thread::get_id()) .arg(holder->query_->makeLabel(holder->query_->getClientId(), nullptr)) .arg(holder->query_->toText()) .arg(holder->thread_); stats::StatsMgr::instance().addValue("pkt6-receive-drop", static_cast(1)); } return (false); } void ClientHandler::lock() { // Sanity check. if (!locked_) { isc_throw(Unexpected, "nothing to lock in ClientHandler::lock"); } add(client_); } void ClientHandler::unLock() { // Sanity check. if (!locked_) { isc_throw(Unexpected, "nothing to unlock in ClientHandler::unLock"); } del(locked_); locked_.reset(); if (!client_ || !client_->cont_) { return; } // Try to process next query. As the caller holds the mutex of // the handler class the continuation will be resumed after. MultiThreadingMgr& mt_mgr = MultiThreadingMgr::instance(); if (mt_mgr.getMode()) { if (!mt_mgr.getThreadPool().addFront(client_->cont_)) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_QUEUE_FULL); } } } } // namespace dhcp } // namespace isc