diff options
author | rfree2monero <rfreemonero@op.pl> | 2015-01-05 20:30:17 +0100 |
---|---|---|
committer | rfree2monero <rfreemonero@op.pl> | 2015-02-20 22:13:00 +0100 |
commit | eabb519605cab00dbaa5a1868d229f09c74570a6 (patch) | |
tree | 42f909d8e94e8cf67836d57cef4053ac572e3f8c /src | |
parent | Merge pull request #229 (diff) | |
download | monero-eabb519605cab00dbaa5a1868d229f09c74570a6.tar.xz |
2014 network limit 1.0a +utils +toc -doc -drmonero
commands and options for network limiting
works very well e.g. for 50 KiB/sec up and down
ToS (QoS) flag
peer number limit
TODO some spikes in ingress/download
TODO problems when other up and down limit
added "otshell utils" - simple logging (with colors, text files channels)
Diffstat (limited to '')
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain_storage.cpp | 26 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain_storage.h | 1 | ||||
-rw-r--r-- | src/cryptonote_protocol/CMakeLists.txt | 46 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp | 266 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.h | 35 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.inl | 73 | ||||
-rw-r--r-- | src/daemon/CMakeLists.txt | 21 | ||||
-rw-r--r-- | src/daemon/daemon_commands_handler.h | 129 | ||||
-rw-r--r-- | src/p2p/CMakeLists.txt | 46 | ||||
-rw-r--r-- | src/p2p/connection_basic.cpp | 362 | ||||
-rw-r--r-- | src/p2p/connection_basic.hpp | 139 | ||||
-rw-r--r-- | src/p2p/net_node.h | 31 | ||||
-rw-r--r-- | src/p2p/net_node.inl | 187 | ||||
-rw-r--r-- | src/p2p/network_throttle-detail.cpp | 382 | ||||
-rw-r--r-- | src/p2p/network_throttle-detail.hpp | 133 | ||||
-rw-r--r-- | src/p2p/network_throttle.cpp | 121 | ||||
-rw-r--r-- | src/p2p/network_throttle.hpp | 187 | ||||
-rw-r--r-- | src/simplewallet/CMakeLists.txt | 1 |
20 files changed, 2113 insertions, 76 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a80dfe378..60501cefa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,6 +92,8 @@ add_subdirectory(cryptonote_core) add_subdirectory(mnemonics) add_subdirectory(rpc) add_subdirectory(wallet) +add_subdirectory(p2p) +add_subdirectory(cryptonote_protocol) add_subdirectory(connectivity_tool) add_subdirectory(miner) diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 3abf93f3c..9eed11874 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -70,6 +70,7 @@ target_link_libraries(cryptonote_core LINK_PUBLIC common crypto + otshell_utils ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index e2b6f2326..78419121f 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -49,6 +49,7 @@ #include "crypto/hash.h" #include "cryptonote_core/checkpoints_create.h" //#include "serialization/json_archive.h" +#include "../../contrib/otshell_utils/utils.hpp" using namespace cryptonote; @@ -1153,6 +1154,31 @@ uint64_t blockchain_storage::block_difficulty(size_t i) return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty; } //------------------------------------------------------------------ +double blockchain_storage::get_avg_block_size( size_t count) +{ + if (count > get_current_blockchain_height()) return 500; + + double average = 0; + _dbg1_c("net/blksize", "HEIGHT: " << get_current_blockchain_height()); + _dbg1_c("net/blksize", "BLOCK ID BY HEIGHT: " << get_block_id_by_height(get_current_blockchain_height()) ); + _dbg1_c("net/blksize", "BLOCK TAIL ID: " << get_tail_id() ); + std::vector<size_t> size_vector; + + get_backward_blocks_sizes(get_current_blockchain_height() - count, size_vector, count); + + std::vector<size_t>::iterator it; + it = size_vector.begin(); + while (it != size_vector.end()) { + average += *it; + _dbg2_c("net/blksize", "VECTOR ELEMENT: " << (*it) ); + it++; + } + average = average / count; + _dbg1_c("net/blksize", "VECTOR SIZE: " << size_vector.size() << " average=" << average); + + return average; +} +//------------------------------------------------------------------ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) { std::stringstream ss; diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 1bfdf7bd0..38bdfbce7 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -134,6 +134,7 @@ namespace cryptonote uint64_t get_current_comulative_blocksize_limit(); bool is_storing_blockchain(){return m_is_blockchain_storing;} uint64_t block_difficulty(size_t i); + double get_avg_block_size( size_t count); template<class t_ids_container, class t_blocks_container, class t_missed_container> bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt new file mode 100644 index 000000000..2ea5662a1 --- /dev/null +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2014, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +cmake_minimum_required (VERSION 2.6) +project (bitmonero CXX) + +file(GLOB CRYPTONOTE_PROTOCOL *) +source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL}) + +#add_library(p2p ${P2P}) + +#bitmonero_private_headers(p2p ${CRYPTONOTE_PROTOCOL}) +bitmonero_add_library(cryptonote_protocol ${CRYPTONOTE_PROTOCOL}) +#target_link_libraries(p2p) +# LINK_PRIVATE +# ${Boost_CHRONO_LIBRARY} +# ${Boost_REGEX_LIBRARY} +# ${Boost_SYSTEM_LIBRARY} +# ${Boost_THREAD_LIBRARY} +# ${EXTRA_LIBRARIES}) +add_dependencies(cryptonote_protocol + version) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp new file mode 100644 index 000000000..6b25cb681 --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -0,0 +1,266 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief This is the place to implement our handlers for protocol network actions, e.g. for ratelimit for download-requests + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <atomic> + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/thread/thread.hpp> + +#include <memory> + +#include "syncobj.h" + +#include "../../contrib/epee/include/net/net_utils_base.h" +#include "../../contrib/epee/include/misc_log_ex.h" +#include <boost/lambda/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/uuid/random_generator.hpp> +#include <boost/chrono.hpp> +#include <boost/utility/value_init.hpp> +#include <boost/asio/deadline_timer.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> +#include "misc_language.h" +#include "pragma_comp_defs.h" +#include <sstream> +#include <iomanip> +#include <algorithm> + + +#include <boost/asio/basic_socket.hpp> +#include <boost/asio/ip/unicast.hpp> + +#include "../../src/cryptonote_protocol/cryptonote_protocol_handler.h" +#include "../../src/p2p/network_throttle.hpp" + +#include "../../contrib/otshell_utils/utils.hpp" +using namespace nOT::nUtils; + +#include "../../../src/cryptonote_core/cryptonote_core.h" // e.g. for the send_stop_signal() + +// ################################################################################################ +// ################################################################################################ +// the "header part". Not separeted out for .hpp because point of this modification is +// to rebuild just 1 translation unit while working on this code. +// (But maybe common parts will be separated out later though - if needed) +// ################################################################################################ +// ################################################################################################ + +namespace cryptonote { + +class cryptonote_protocol_handler_base_pimpl { // placeholer if needed + public: + +}; + +} // namespace + +// ################################################################################################ +// ################################################################################################ +// ################################################################################################ +// ################################################################################################ + +namespace cryptonote { + +double cryptonote_protocol_handler_base::estimate_one_block_size() noexcept { // for estimating size of blocks to downloa + const double size_min = 500; // XXX 500 + const int history_len = 20; // how many blocks to average over + + double avg=0; + try { + avg = get_avg_block_size(history_len); + } catch (...) { } + avg = std::max( size_min , avg); + return avg; +} + +cryptonote_protocol_handler_base::cryptonote_protocol_handler_base() { +} + +cryptonote_protocol_handler_base::~cryptonote_protocol_handler_base() { +} + +void cryptonote_protocol_handler_base::handler_request_blocks_now(size_t &count_limit) { + using namespace epee::net_utils; + size_t est_req_size=0; // how much data are we now requesting (to be soon send to us) + + const auto count_limit_default = count_limit; + + bool allowed_now = false; // are we now allowed to request or are we limited still + // long int size_limit; + + while (!allowed_now) { + /* if ( ::cryptonote::core::get_is_stopping() ) { // TODO fast exit + _fact("ABORT sleep (before sending requeset) due to stopping"); + break; + }*/ + + //LOG_PRINT_RED("[DBG]" << get_avg_block_size(1), LOG_LEVEL_0); + //{ + long int size_limit1=0, size_limit2=0; + //LOG_PRINT_RED("calculating REQUEST size:", LOG_LEVEL_0); + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_in ); + network_throttle_manager::get_global_throttle_in().tick(); + size_limit1 = network_throttle_manager::get_global_throttle_in().get_recommended_size_of_planned_transport(); + } + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_inreq ); + network_throttle_manager::get_global_throttle_inreq().tick(); + size_limit2 = network_throttle_manager::get_global_throttle_inreq().get_recommended_size_of_planned_transport(); + } + + long int one_block_estimated_size = estimate_one_block_size(); + long int limit_small = std::min( size_limit1 , size_limit2 ); + long int size_limit = limit_small/3 + size_limit1/3 + size_limit2/3; + if (limit_small <= 0) size_limit = 0; + const double estimated_peers = 1.2; // how many peers/threads we want to talk to, in order to not grab entire b/w by 1 thread + const double knob = 1.000; + size_limit /= (estimated_peers / estimated_peers) * knob; + _note_c("net/req-calc" , "calculating REQUEST size:" << size_limit1 << " " << size_limit2 << " small=" << limit_small << " final size_limit="<<size_limit); + + double L = size_limit / one_block_estimated_size; // calculating item limit (some heuristics) + //LOG_PRINT_RED("L1 = " << L , LOG_LEVEL_0); + //double L2=0; if (L>1) L2=std::log(L); + //L = L/10. + L2*5; + //LOG_PRINT_RED("L2 = " << L , LOG_LEVEL_0); + L = std::min( (double)count_limit_default, (double)L); + //LOG_PRINT_RED("L3 = " << L , LOG_LEVEL_0); + + const long int hard_limit = 500; // never get more blocks at once ; TODO depend on speed limit. Must be low or limiting is too bursty. + + L = std::min(L, (double) hard_limit); + + count_limit = (int)L; + + est_req_size = count_limit * one_block_estimated_size ; // how much data did we just requested? + + //LOG_PRINT_RED("est_req_size = " << est_req_size , LOG_LEVEL_0); + //LOG_PRINT_RED("count_limit = " << count_limit , LOG_LEVEL_0); + //LOG_PRINT_RED("one_block_estimated_size = " << one_block_estimated_size , LOG_LEVEL_0); + //} + + if (count_limit > 0) allowed_now = true; + // XXX if (!allowed_now) { // XXX DOWNLOAD + //long int ms = 3000; // XXX 2000 + //LOG_PRINT_RED("size_limit = " << size_limit , LOG_LEVEL_0); + long int ms = network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick(one_block_estimated_size); // XXX too long + //long int ms = network_throttle_manager::get_global_throttle_in().get_sleep_time(count_limit); // XXX + //long int ms = network_throttle_manager::get_global_throttle_in().get_sleep_time(size_limit); // XXX best + + //ms /= 100; // XXX + _info_c("net/sleep", "Sleeping in " << __FUNCTION__ << " for " << ms << " ms"); // XXX debug sleep + //LOG_PRINT_RED("ms = " << ms , LOG_LEVEL_0); + boost::this_thread::sleep(boost::posix_time::milliseconds( ms ) ); // TODO randomize sleeps + //} + } + // done waiting&sleeping ^ + + // ok we are allowed to send now + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_inreq ); + network_throttle_manager::get_global_throttle_inreq().handle_trafic_tcp( est_req_size ); // increase countere of the global requested input + } + + // TODO remove debug + LOG_PRINT_YELLOW("*************************************************************************", LOG_LEVEL_0); + LOG_PRINT_RED("### RRRR ### sending request (type 1), CALCULATED limit = " << count_limit << " = estimated " << est_req_size << " b", LOG_LEVEL_0); + LOG_PRINT_YELLOW("*************************************************************************", LOG_LEVEL_0); + LOG_PRINT_RED("\n", LOG_LEVEL_0); + _note_c("net/req", "### RRRR ### sending request (type 1), CALCULATED limit = " << count_limit << " = estimated " << est_req_size << " b"); +} + +void cryptonote_protocol_handler_base::handler_request_blocks_history(std::list<crypto::hash>& ids) { + using namespace epee::net_utils; + LOG_PRINT_L0("### ~~~RRRR~~~~ ### sending request (type 2), limit = " << ids.size()); + LOG_PRINT_RED("RATE LIMIT NOT IMPLEMENTED HERE YET (download at unlimited speed?)" , LOG_LEVEL_0); + _note_c("net/req2", "### ~~~RRRR~~~~ ### sending request (type 2), limit = " << ids.size()); + // TODO +} + +void cryptonote_protocol_handler_base::handler_response_blocks_now(size_t packet_size) { _scope_mark(""); + using namespace epee::net_utils; + double delay=0; // will be calculated + _dbg1("Packet size: " << packet_size); + do + { // rate limiting + //XXX + /*if (::cryptonote::core::get_is_stopping()) { + _dbg1("We are stopping - so abort sleep"); + return; + }*/ + /*if (m_was_shutdown) { + _dbg2_c("net/netuse/sleep","m_was_shutdown - so abort sleep"); + return; + }*/ + + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); + delay = network_throttle_manager::get_global_throttle_out().get_sleep_time_after_tick( packet_size ); // decission from global + } + + + delay *= 0.50; + //delay = 0; // XXX + if (delay > 0) { + //delay += rand2*0.1; + long int ms = (long int)(delay * 1000); + _info_c("net/sleep", "Sleeping in " << __FUNCTION__ << " for " << ms << " ms before packet_size="<<packet_size); // XXX debug sleep + _dbg1_c("net/sleep/", "sleep in sleep_before_packet"); + _dbg2("Sleep for " << ms); + boost::this_thread::sleep(boost::posix_time::milliseconds( ms ) ); // TODO randomize sleeps + } + } while(delay > 0); + +// XXX LATER XXX + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); + network_throttle_manager::get_global_throttle_out().handle_trafic_tcp( packet_size ); // increase counter - global + //epee::critical_region_t<decltype(m_throttle_global_lock)> guard(m_throttle_global_lock); // *** critical *** + //m_throttle_global.m_out.handle_trafic_tcp( packet_size ); // increase counter - global + } +} + +} // namespace + + diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index a3b79856e..b3393928c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -1,3 +1,7 @@ +/// @file +/// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) +/// @brief This is the orginal cryptonote protocol network-events handler, modified by us + // Copyright (c) 2014-2015, The Monero Project // // All rights reserved. @@ -41,6 +45,7 @@ #include "cryptonote_core/connection_context.h" #include "cryptonote_core/cryptonote_stat_info.h" #include "cryptonote_core/verification_context.h" +#include <netinet/in.h> PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -48,8 +53,26 @@ DISABLE_VS_WARNINGS(4355) namespace cryptonote { + class cryptonote_protocol_handler_base_pimpl; + class cryptonote_protocol_handler_base { + private: + std::unique_ptr<cryptonote_protocol_handler_base_pimpl> mI; + + public: + cryptonote_protocol_handler_base(); + virtual ~cryptonote_protocol_handler_base(); + void handler_request_blocks_now(size_t & count_limit); // before asking for blocks, can adjust the limit of download + void handler_request_blocks_history(std::list<crypto::hash>& ids); // before asking for list of objects, we can change the list still + void handler_response_blocks_now(size_t packet_size); + + virtual double get_avg_block_size( size_t count) const = 0; + virtual double estimate_one_block_size() noexcept; // for estimating size of blocks to download + + virtual std::ofstream& get_logreq() const =0; + }; + template<class t_core> - class t_cryptonote_protocol_handler: public i_cryptonote_protocol + class t_cryptonote_protocol_handler: public i_cryptonote_protocol, cryptonote_protocol_handler_base { public: typedef cryptonote_connection_context connection_context; @@ -107,12 +130,17 @@ namespace cryptonote std::atomic<uint32_t> m_syncronized_connections_count; std::atomic<bool> m_synchronized; + // static std::ofstream m_logreq; + + double get_avg_block_size(size_t count) const; + template<class t_parametr> bool post_notify(typename t_parametr::request& arg, cryptonote_connection_context& context) { LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(context) << "] post " << typeid(t_parametr).name() << " -->"); std::string blob; epee::serialization::store_t_to_binary(arg, blob); + //handler_response_blocks_now(blob.size()); // XXX return m_p2p->invoke_notify_to_peer(t_parametr::ID, blob, context); } @@ -124,8 +152,11 @@ namespace cryptonote epee::serialization::store_t_to_binary(arg, arg_buff); return m_p2p->relay_notify_to_all(t_parametr::ID, arg_buff, exlude_context); } + + virtual std::ofstream& get_logreq() const ; }; -} + +} // namespace #include "cryptonote_protocol_handler.inl" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 2754eb73c..aebfcd10d 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1,3 +1,7 @@ +/// @file +/// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) +/// @brief This is the orginal cryptonote protocol network-events handler, modified by us + // Copyright (c) 2014-2015, The Monero Project // // All rights reserved. @@ -28,14 +32,26 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +// (may contain code and/or modifications by other developers) +// developer rfree: this code is caller of our new network code, and is modded; e.g. for rate limiting + #include <boost/interprocess/detail/atomic.hpp> #include <list> #include "cryptonote_core/cryptonote_format_utils.h" #include "profile_tools.h" +#include "../../contrib/otshell_utils/utils.hpp" +using namespace nOT::nUtils; + namespace cryptonote { + +// static +// template<class t_core> std::ofstream t_cryptonote_protocol_handler<t_core>::m_logreq("logreq.txt"); // static + + + //----------------------------------------------------------------------------------------------------------------------- template<class t_core> t_cryptonote_protocol_handler<t_core>::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint<connection_context>* p_net_layout):m_core(rcore), @@ -100,20 +116,24 @@ namespace cryptonote { std::stringstream ss; - ss << std::setw(25) << std::left << "Remote Host" + ss << std::setw(30) << std::left << "Remote Host" << std::setw(20) << "Peer id" << std::setw(25) << "Recv/Sent (inactive,sec)" << std::setw(25) << "State" << std::setw(20) << "Livetime(seconds)" << ENDL; + uint32_t ip; m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) { - ss << std::setw(25) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + - epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) + ip = ntohl(cntxt.m_remote_ip); + ss << std::setw(30) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + + epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) << std::setw(20) << std::hex << peer_id << std::setw(25) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")" << std::setw(25) << get_protocol_state_string(cntxt.m_state) - << std::setw(20) << std::to_string(time(NULL) - cntxt.m_started) << ENDL; + << std::setw(20) << std::to_string(time(NULL) - cntxt.m_started) + << std::setw(10) << (ip > 3232235520 && ip < 3232301055 ? " [LAN]" : "") //TODO: local ip in calss A, B + << ENDL; return true; }); LOG_PRINT_L0("Connections: " << ENDL << ss.str()); @@ -234,11 +254,11 @@ namespace cryptonote block_verification_context bvc = boost::value_initialized<block_verification_context>(); m_core.pause_mine(); - m_core.handle_incoming_block(arg.b.block, bvc); + m_core.handle_incoming_block(arg.b.block, bvc); // got block from handle_notify_new_block m_core.resume_mine(); if(bvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); + LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; } @@ -304,9 +324,19 @@ namespace cryptonote LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size()); post_notify<NOTIFY_RESPONSE_GET_OBJECTS>(rsp, context); + //handler_response_blocks_now(sizeof(rsp)); // XXX + //handler_response_blocks_now(200); return 1; } //------------------------------------------------------------------------------------------------------------------------ + + + template<class t_core> + double t_cryptonote_protocol_handler<t_core>::get_avg_block_size( size_t count) const { + return m_core.get_blockchain_storage().get_avg_block_size(count); + } + + template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { @@ -378,6 +408,7 @@ namespace cryptonote epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( boost::bind(&t_core::resume_mine, &m_core)); + LOG_PRINT_CCONTEXT_YELLOW( "Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size() , LOG_LEVEL_0); BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) { //process transactions @@ -419,7 +450,8 @@ namespace cryptonote LOG_PRINT_CCONTEXT_L2("Block process time: " << block_process_time + transactions_process_time << "(" << transactions_process_time << "/" << block_process_time << ")ms"); } } - + size_t count_limit = BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; + handler_request_blocks_now(count_limit); // XXX request_missing_objects(context, true); return 1; } @@ -455,6 +487,11 @@ namespace cryptonote size_t count = 0; auto it = context.m_needed_objects.begin(); + size_t count_limit = BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; + //handler_request_blocks_now( count_limit ); // change the limit, sleep(?) XXX + // XXX + count_limit=200; // XXX + _note_c("net/req-calc" , "Setting count_limit: " << count_limit); while(it != context.m_needed_objects.end() && count < BLOCKS_SYNCHRONIZING_DEFAULT_COUNT) { if( !(check_having_blocks && m_core.have_block(*it))) @@ -465,14 +502,16 @@ namespace cryptonote } context.m_needed_objects.erase(it++); } - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size()); + LOG_PRINT_CCONTEXT_L0("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size() + << "requested blocks count=" << count << " / " << count_limit); post_notify<NOTIFY_REQUEST_GET_OBJECTS>(req, context); }else if(context.m_last_response_height < context.m_remote_blockchain_height-1) {//we have to fetch more objects ids, request blockchain entry NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>(); m_core.get_short_chain_history(r.block_ids); - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?) + LOG_PRINT_CCONTEXT_L0("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); post_notify<NOTIFY_REQUEST_CHAIN>(r, context); }else { @@ -575,4 +614,18 @@ namespace cryptonote { return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context); } -} + + /// @deprecated + template<class t_core> std::ofstream& t_cryptonote_protocol_handler<t_core>::get_logreq() const { + static std::ofstream * logreq=NULL; + if (!logreq) { + LOG_PRINT_RED("LOG OPENED",LOG_LEVEL_0); + logreq = new std::ofstream("logreq.txt"); // leak mem (singleton) + *logreq << "Opened log" << std::endl; + } + LOG_PRINT_YELLOW("LOG USED",LOG_LEVEL_0); + (*logreq) << "log used" << std::endl; + return *logreq; + } + +} // namespace diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 5f60857d6..4a0dcb148 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -32,23 +32,7 @@ set(daemon_sources set(daemon_headers) set(daemon_private_headers - daemon_commands_handler.h - - # cryptonote_protocol - ../cryptonote_protocol/blobdatatype.h - ../cryptonote_protocol/cryptonote_protocol_defs.h - ../cryptonote_protocol/cryptonote_protocol_handler.h - ../cryptonote_protocol/cryptonote_protocol_handler.inl - ../cryptonote_protocol/cryptonote_protocol_handler_common.h - - # p2p - ../p2p/net_node.h - ../p2p/net_node.inl - ../p2p/net_node_common.h - ../p2p/net_peerlist.h - ../p2p/net_peerlist_boost_serialization.h - ../p2p/p2p_protocol_defs.h - ../p2p/stdafx.h) + daemon_commands_handler.h) bitmonero_private_headers(daemon ${daemon_private_headers}) @@ -62,6 +46,9 @@ target_link_libraries(daemon cryptonote_core crypto common + otshell_utils + p2p + cryptonote_protocol ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index 10a07cf89..7cba4ec5a 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -40,6 +40,7 @@ #include "common/util.h" #include "crypto/hash.h" #include "version.h" +#include "../../contrib/otshell_utils/utils.hpp" /*! * \brief I don't really know right now @@ -74,6 +75,10 @@ public: m_cmd_binder.set_handler("save", boost::bind(&daemon_cmmands_handler::save, this, _1), "Save blockchain"); m_cmd_binder.set_handler("set_log", boost::bind(&daemon_cmmands_handler::set_log, this, _1), "set_log <level> - Change current log detalization level, <level> is a number 0-4"); m_cmd_binder.set_handler("diff", boost::bind(&daemon_cmmands_handler::diff, this, _1), "Show difficulty"); + m_cmd_binder.set_handler("out_peers", boost::bind(&daemon_cmmands_handler::out_peers_limit, this, _1), "Set max limit of out peers"); + m_cmd_binder.set_handler("limit_up", boost::bind(&daemon_cmmands_handler::limit_up, this, _1), "Set upload limit [kB/s]"); + m_cmd_binder.set_handler("limit_down", boost::bind(&daemon_cmmands_handler::limit_down, this, _1), "Set download limit [kB/s]"); + m_cmd_binder.set_handler("limit", boost::bind(&daemon_cmmands_handler::limit, this, _1), "Set download and upload limit [kB/s]"); } bool start_handling() @@ -398,4 +403,128 @@ private: m_srv.get_payload_object().get_core().get_miner().stop(); return true; } + //-------------------------------------------------------------------------------- + bool out_peers_limit(const std::vector<std::string>& args) { + if(args.size()!=1) { + std::cout << "Usage: limit_down <speed>" << ENDL; + return true; + } + + unsigned int limit; + try { + limit = std::stoi(args[0]); + } + + catch(std::invalid_argument& ex) { + _erro("stoi exception"); + return false; + } + + if (m_srv.m_config.m_net_config.connections_count > limit) + { + int count = m_srv.m_config.m_net_config.connections_count - limit; + m_srv.m_config.m_net_config.connections_count = limit; + m_srv.delete_connections(count); + } + else + m_srv.m_config.m_net_config.connections_count = limit; + + return true; + } + //-------------------------------------------------------------------------------- + bool limit_up(const std::vector<std::string>& args) + { + if(args.size()!=1) { + std::cout << "Usage: limit_up <speed>" << ENDL; + return false; + } + + int limit; + try { + limit = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + + if (limit==-1) { + limit=128; + //this->islimitup=false; + } + + limit *= 1024; + + + //nodetool::epee::net_utils::connection<epee::levin::async_protocol_handler<nodetool::p2p_connection_context> >::set_rate_up_limit( limit ); + epee::net_utils::connection_basic::set_rate_up_limit( limit ); + std::cout << "Set limit-up to " << limit/1024 << " kB/s" << std::endl; + + return true; + } + + //-------------------------------------------------------------------------------- + bool limit_down(const std::vector<std::string>& args) + { + + if(args.size()!=1) { + std::cout << "Usage: limit_down <speed>" << ENDL; + return true; + } + + int limit; + try { + limit = std::stoi(args[0]); + } + + catch(std::invalid_argument& ex) { + return false; + } + + if (limit==-1) { + limit=128; + //this->islimitup=false; + } + + limit *= 1024; + + + //nodetool::epee::net_utils::connection<epee::levin::async_protocol_handler<nodetool::p2p_connection_context> >::set_rate_up_limit( limit ); + epee::net_utils::connection_basic::set_rate_down_limit( limit ); + std::cout << "Set limit-down to " << limit/1024 << " kB/s" << std::endl; + + return true; + } + + //-------------------------------------------------------------------------------- + bool limit(const std::vector<std::string>& args) + { + if(args.size()!=1) { + std::cout << "Usage: limit_down <speed>" << ENDL; + return true; + } + + int limit; + try { + limit = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + + if (limit==-1) { + limit=128; + //this->islimitup=false; + } + + limit *= 1024; + + + //nodetool::epee::net_utils::connection<epee::levin::async_protocol_handler<nodetool::p2p_connection_context> >::set_rate_up_limit( limit ); + epee::net_utils::connection_basic::set_rate_down_limit( limit ); + epee::net_utils::connection_basic::set_rate_up_limit( limit ); + std::cout << "Set limit-down to " << limit/1024 << " kB/s" << std::endl; + std::cout << "Set limit-up to " << limit/1024 << " kB/s" << std::endl; + + return true; + } }; diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt new file mode 100644 index 000000000..541b90fa9 --- /dev/null +++ b/src/p2p/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2014, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +cmake_minimum_required (VERSION 2.6) +project (bitmonero CXX) + +file(GLOB P2P *) +source_group(p2p FILES ${P2P}) + +#add_library(p2p ${P2P}) + +#bitmonero_private_headers(p2p ${P2P}) +bitmonero_add_library(p2p ${P2P}) +#target_link_libraries(p2p) +# LINK_PRIVATE +# ${Boost_CHRONO_LIBRARY} +# ${Boost_REGEX_LIBRARY} +# ${Boost_SYSTEM_LIBRARY} +# ${Boost_THREAD_LIBRARY} +# ${EXTRA_LIBRARIES}) +add_dependencies(p2p + version) diff --git a/src/p2p/connection_basic.cpp b/src/p2p/connection_basic.cpp new file mode 100644 index 000000000..35b0d4c8e --- /dev/null +++ b/src/p2p/connection_basic.cpp @@ -0,0 +1,362 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief base for connection, contains e.g. the ratelimit hooks + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* rfree: implementation for the non-template base, can be used by connection<> template class in abstract_tcp_server2 file */ + +#include "connection_basic.hpp" + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <atomic> + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/thread/thread.hpp> + +#include <memory> + +#include "syncobj.h" + +#include "../../contrib/epee/include/net/net_utils_base.h" +#include "../../contrib/epee/include/misc_log_ex.h" +#include <boost/lambda/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/uuid/random_generator.hpp> +#include <boost/chrono.hpp> +#include <boost/utility/value_init.hpp> +#include <boost/asio/deadline_timer.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> +#include <boost/filesystem.hpp> +#include "misc_language.h" +#include "pragma_comp_defs.h" +#include <fstream> +#include <sstream> +#include <iomanip> +#include <algorithm> +#include <mutex> + +#include <boost/asio/basic_socket.hpp> +#include <boost/asio/ip/unicast.hpp> +#include "../../contrib/epee/include/net/abstract_tcp_server2.h" + +#include "../../contrib/otshell_utils/utils.hpp" +using namespace nOT::nUtils; + +// TODO: +#include "../../src/p2p/network_throttle-detail.hpp" +#include "../../src/cryptonote_core/cryptonote_core.h" + +// ################################################################################################ +// local (TU local) headers +// ################################################################################################ + +namespace epee +{ +namespace net_utils +{ + + +/* ============================================================================ */ + +class connection_basic_pimpl { + public: + connection_basic_pimpl(const std::string &name); + + static int m_default_tos; + + network_throttle_bw m_throttle; // per-perr + critical_section m_throttle_lock; + + int m_peer_number; // e.g. for debug/stats +}; + + +} // namespace +} // namespace + +// ################################################################################################ +// The implementation part +// ################################################################################################ + +namespace epee +{ +namespace net_utils +{ + +// ================================================================================================ +// connection_basic_pimpl +// ================================================================================================ + +connection_basic_pimpl::connection_basic_pimpl(const std::string &name) : m_throttle(name) { } + +// ================================================================================================ +// connection_basic +// ================================================================================================ + +// static variables: +int connection_basic_pimpl::m_default_tos; + +// methods: +connection_basic::connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number) + : + mI( new connection_basic_pimpl("peer") ), + strand_(io_service), + socket_(io_service), + m_want_close_connection(false), + m_was_shutdown(false), + m_ref_sock_count(ref_sock_count) +{ + ++ref_sock_count; // increase the global counter + mI->m_peer_number = sock_number.fetch_add(1); // use, and increase the generated number + _note("Spawned connection p2p#"<<mI->m_peer_number<<" currently we have sockets count:" << m_ref_sock_count); + boost::filesystem::create_directories("log/dr-monero/net/"); + /*boost::asio::SettableSocketOption option;// = new boost::asio::SettableSocketOption(); + option.level(IPPROTO_IP); + option.name(IP_TOS); + option.value(&tos); + option.size = sizeof(tos); + socket_.set_option(option);*/ + // TODO socket options +} + +connection_basic::~connection_basic() { + _note("Destructing connection p2p#"<<mI->m_peer_number); +} + +void connection_basic::set_rate_up_limit(uint64_t limit) { + save_limit_to_file(limit); + { + // TODO remove __SCALING_FACTOR... + const double SCALING_FACTOR = 2.25; // to acheve the best performance + limit *= SCALING_FACTOR; + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); + network_throttle_manager::get_global_throttle_out().set_target_speed(limit); + } + // connection_basic_pimpl::m_throttle_global.m_out.set_target_speed(limit); +} + +void connection_basic::set_rate_down_limit(uint64_t limit) { + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_in ); + network_throttle_manager::get_global_throttle_in().set_target_speed(limit); + } + + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_inreq ); + network_throttle_manager::get_global_throttle_inreq().set_target_speed(limit); + } + save_limit_to_file(limit); +} + +void connection_basic::set_rate_limit(uint64_t limit) { + // TODO +} +void connection_basic::set_kill_limit (uint64_t limit) { + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_in ); + network_throttle_manager::get_global_throttle_in().set_target_kill(limit); + } + + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); + network_throttle_manager::get_global_throttle_out().set_target_kill(limit); + } + + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_inreq ); + network_throttle_manager::get_global_throttle_inreq().set_target_kill(limit); + } +} + +void connection_basic::save_limit_to_file(int limit) { + // saving limit to file + std::ofstream file; + file.open("log/dr-monero/limit.info"); + file << limit; +} + +void connection_basic::set_rate_autodetect(uint64_t limit) { + // TODO + LOG_PRINT_L0("inside connection_basic we set autodetect (this is additional notification).."); +} + +void connection_basic::set_tos_flag(int tos) { + connection_basic_pimpl::m_default_tos = tos; +} + +int connection_basic::get_tos_flag() { + return connection_basic_pimpl::m_default_tos; +} + +void connection_basic::sleep_before_packet(size_t packet_size, int phase, int q_len) { + double delay=0; // will be calculated + do + { // rate limiting + //XXX + /*if (::cryptonote::core::get_is_stopping()) { + _dbg1("We are stopping - so abort sleep"); + return; + }*/ + if (m_was_shutdown) { + _dbg2("m_was_shutdown - so abort sleep"); + return; + } + + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); + delay = network_throttle_manager::get_global_throttle_out().get_sleep_time_after_tick( packet_size ); // decission from global + } + + + delay *= 0.50; + delay = 0; // XXX + if (delay > 0) { + //delay += rand2*0.1; + long int ms = (long int)(delay * 1000); + _info_c("net/sleep", "Sleeping in " << __FUNCTION__ << " for " << ms << " ms before packet_size="<<packet_size); // XXX debug sleep + _dbg1("sleep in sleep_before_packet"); + boost::this_thread::sleep(boost::posix_time::milliseconds( ms ) ); // TODO randomize sleeps + } + } while(delay > 0); + +// XXX LATER XXX + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); + network_throttle_manager::get_global_throttle_out().handle_trafic_tcp( packet_size ); // increase counter - global + //epee::critical_region_t<decltype(m_throttle_global_lock)> guard(m_throttle_global_lock); // *** critical *** + //m_throttle_global.m_out.handle_trafic_tcp( packet_size ); // increase counter - global + } + +} +void connection_basic::set_start_time() { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); + m_start_time = network_throttle_manager::get_global_throttle_out().get_time_seconds(); +} + +void connection_basic::do_send_handler_start(const void* ptr , size_t cb ) { + _fact_c("net/out/size", "*** do_sen() called for packet="<<cb<<" B"); + sleep_before_packet(cb,1,-1); + // set_start_time(); +} + +void connection_basic::do_send_handler_delayed(const void* ptr , size_t cb ) { + // CRITICAL_REGION_LOCAL(network_throttle_manager::m_lock_get_global_throttle_out); + // auto sending_time = network_throttle_manager::get_global_throttle_out().get_time_seconds() - m_start_time; // wrong? --r +} + +void connection_basic::do_send_handler_write(const void* ptr , size_t cb ) { + sleep_before_packet(cb,1,-1); + _info_c("net/out/size", "handler_write (direct) - before ASIO write, for packet="<<cb<<" B (after sleep)"); + set_start_time(); +} + +void connection_basic::do_send_handler_stop(const void* ptr , size_t cb ) { +} + +void connection_basic::do_send_handler_after_write(const boost::system::error_code& e, size_t cb) { + // CRITICAL_REGION_LOCAL(network_throttle_manager::m_lock_get_global_throttle_out); + // auto sending_time = network_throttle_manager::get_global_throttle_out().get_time_seconds() - m_start_time; + // lag: if current sending time > max sending time + //if (sending_time > 0.1) network_throttle_manager::get_global_throttle_out().set_overheat(sending_time); // TODO + +} + +void connection_basic::do_send_handler_write_from_queue( const boost::system::error_code& e, size_t cb, int q_len ) { + sleep_before_packet(cb,2,q_len); + _info_c("net/out/size", "handler_write (after write, from queue="<<q_len<<") - before ASIO write, for packet="<<cb<<" B (after sleep)"); + + set_start_time(); +} + +void connection_basic::do_read_handler_start(const boost::system::error_code& e, std::size_t bytes_transferred) { // from read, after read completion + const size_t packet_size = bytes_transferred; + { + CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_in ); + // sleep_before_packet(packet_size * __SCALING_FACTOR, 1, -1); // TODO remove __SCALING_FACTOR + network_throttle_manager::get_global_throttle_in().handle_trafic_tcp( packet_size ); // increase counter - global + // epee::critical_region_t<decltype(mI->m_throttle_global_lock)> guard(mI->m_throttle_global_lock); // *** critical *** + // mI->m_throttle_global.m_in.handle_trafic_tcp( packet_size ); // increase counter - global + } +} + +void connection_basic::logger_handle_net_peer(size_t size, bool io) { // network data written + // TODO OPTIMIZE! do NOT reopen idiotically :) + std::ostringstream oss; + std::string filename; + if (io) { // write + double time = network_throttle_manager::get_global_throttle_in().get_time_seconds() ; + oss << "log/dr-monero/net/in-peer-" << (mI->m_peer_number) << ".dat" << std::ends; + filename = oss.str(); + network_throttle_manager::get_global_throttle_out().logger_handle_net(filename,time,size); + } + else { // read + double time = network_throttle_manager::get_global_throttle_out().get_time_seconds() ; + oss << "log/dr-monero/net/out-peer-" << (mI->m_peer_number) << ".dat" << std::ends; + filename = oss.str(); + network_throttle_manager::get_global_throttle_in().logger_handle_net(filename,time,size); + } +} + +void connection_basic::logger_handle_net_read(size_t size) { // network data read + std::string filename = "log/dr-monero/net/in-all.data"; + + double time = network_throttle_manager::get_global_throttle_in().get_time_seconds() ; + network_throttle_manager::get_global_throttle_in().logger_handle_net(filename, time, size); + logger_handle_net_peer(size,0); +} + +void connection_basic::logger_handle_net_write(size_t size) { + std::string filename = "log/dr-monero/net/out-all.data"; + double time = network_throttle_manager::get_global_throttle_out().get_time_seconds() ; + network_throttle_manager::get_global_throttle_out().logger_handle_net(filename, time, size); + logger_handle_net_peer(size,1); + +} + +double connection_basic::get_sleep_time(size_t cb) { + auto t = network_throttle_manager::get_global_throttle_out().get_sleep_time(cb); + return t; +} + + +} // namespace +} // namespace + diff --git a/src/p2p/connection_basic.hpp b/src/p2p/connection_basic.hpp new file mode 100644 index 000000000..1b5a2c8ad --- /dev/null +++ b/src/p2p/connection_basic.hpp @@ -0,0 +1,139 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief base for connection, contains e.g. the ratelimit hooks + +// ! This file might contain variable names same as in template class connection<> +// ! from files contrib/epee/include/net/abstract_tcp_server2.* +// ! I am not a lawyer; afaik APIs, var names etc are not copyrightable ;) +// ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part) +// ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as: + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +/* rfree: place for hanlers for the non-template base, can be used by connection<> template class in abstract_tcp_server2 file */ + +#ifndef INCLUDED_p2p_connection_basic_hpp +#define INCLUDED_p2p_connection_basic_hpp + + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <atomic> + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/thread/thread.hpp> + +#include <memory> + +#include "../../contrib/epee/include/net/net_utils_base.h" +#include "../../contrib/epee/include/syncobj.h" + +namespace epee +{ +namespace net_utils +{ + + /************************************************************************/ + /* */ + /************************************************************************/ + /// Represents a single connection from a client. + +class connection_basic_pimpl; // PIMPL for this class + +class connection_basic { // not-templated base class for rapid developmet of some code parts + public: + std::unique_ptr< connection_basic_pimpl > mI; // my Implementation + + // moved here from orginal connecton<> - common member variables that do not depend on template in connection<> + volatile uint32_t m_want_close_connection; + std::atomic<bool> m_was_shutdown; + critical_section m_send_que_lock; + std::list<std::string> m_send_que; + volatile bool m_is_multithreaded; + double m_start_time; + /// Strand to ensure the connection's handlers are not called concurrently. + boost::asio::io_service::strand strand_; + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + std::atomic<long> &m_ref_sock_count; // reference to external counter of existing sockets that we will ++/-- + public: + // first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator + connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number); + + virtual ~connection_basic(); + + // various handlers to be called from connection class: + void do_send_handler_start(const void * ptr , size_t cb); + void do_send_handler_delayed(const void * ptr , size_t cb); + void do_send_handler_write(const void * ptr , size_t cb); + void do_send_handler_stop(const void * ptr , size_t cb); + void do_send_handler_after_write( const boost::system::error_code& e, size_t cb ); // from handle_write + void do_send_handler_write_from_queue(const boost::system::error_code& e, size_t cb , int q_len); // from handle_write, sending next part + void do_read_handler_start(const boost::system::error_code& e, std::size_t bytes_transferred); // from read, after read completion + + void logger_handle_net_write(size_t size); // network data written + void logger_handle_net_read(size_t size); // network data read + void logger_handle_net_peer(size_t size, bool io); + + void set_start_time(); + + // config for rate limit + + static void set_rate_up_limit(uint64_t limit); + static void set_rate_down_limit(uint64_t limit); + static void set_rate_limit(uint64_t limit); + static void set_rate_autodetect(uint64_t limit); + static void set_kill_limit (uint64_t limit); + + // config misc + static void set_tos_flag(int tos); // ToS / QoS flag + static int get_tos_flag(); + + // handlers and sleep + void sleep_before_packet(size_t packet_size, int phase, int q_len); // execute a sleep ; phase is not really used now(?) + static void save_limit_to_file(int limit); ///< for dr-monero + static double get_sleep_time(size_t cb); +}; + +} // nameserver +} // nameserver + +#endif + + diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 97fcd56c8..48737193e 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -80,14 +80,12 @@ namespace nodetool public: typedef t_payload_net_handler payload_net_handler; - node_server( - t_payload_net_handler& payload_handler - , boost::uuids::uuid network_id - ) - : m_payload_handler(payload_handler) - , m_allow_local_ip(false) - , m_hide_my_port(false) - , m_network_id(std::move(network_id)) + node_server(t_payload_net_handler& payload_handler, boost::uuids::uuid network_id) + :m_payload_handler(payload_handler), + m_allow_local_ip(false), + m_no_igd(false), + m_hide_my_port(false), + m_network_id(std::move(network_id)) {} static void init_options(boost::program_options::options_description& desc); @@ -111,6 +109,7 @@ namespace nodetool virtual uint64_t get_connections_count(); size_t get_outgoing_connections_count(); peerlist_manager& get_peerlist_manager(){return m_peerlist;} + void delete_connections(size_t count); private: const std::vector<std::string> m_seed_nodes_list = { "seeds.moneroseeds.se" @@ -118,6 +117,9 @@ namespace nodetool , "seeds.moneroseeds.ch" , "seeds.moneroseeds.li" }; + + bool islimitup=false; + bool islimitdown=false; typedef COMMAND_REQUEST_STAT_INFO_T<typename t_payload_net_handler::stat_info> COMMAND_REQUEST_STAT_INFO; @@ -197,6 +199,13 @@ namespace nodetool template <class Container> bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container); + bool set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max); + bool set_tos_flag(const boost::program_options::variables_map& vm, int limit); + + bool set_rate_up_limit(const boost::program_options::variables_map& vm, int64_t limit); + bool set_rate_down_limit(const boost::program_options::variables_map& vm, int64_t limit); + bool set_rate_limit(const boost::program_options::variables_map& vm, uint64_t limit); + //debug functions std::string print_connections_container(); @@ -214,7 +223,10 @@ namespace nodetool END_KV_SERIALIZE_MAP() }; - config m_config; + public: + config m_config; // TODO was private, add getters? + + private: std::string m_config_folder; bool m_have_address; @@ -224,6 +236,7 @@ namespace nodetool uint32_t m_ip_address; bool m_allow_local_ip; bool m_hide_my_port; + bool m_no_igd; //critical_section m_connections_lock; //connections_indexed_container m_connections; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index ee4a10789..ce70e241a 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -84,6 +84,14 @@ namespace nodetool " If this option is given the options add-priority-node and seed-node are ignored"}; const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + + const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; + const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max limit of out peers", -1}; + const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1}; + + const command_line::arg_descriptor<int64_t> arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", -1}; + const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", -1}; + const command_line::arg_descriptor<uint64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", 128}; } //----------------------------------------------------------------------------------- @@ -99,7 +107,13 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_add_priority_node); command_line::add_arg(desc, arg_p2p_add_exclusive_node); command_line::add_arg(desc, arg_p2p_seed_node); - command_line::add_arg(desc, arg_p2p_hide_my_port); } + command_line::add_arg(desc, arg_p2p_hide_my_port); + command_line::add_arg(desc, arg_no_igd); + command_line::add_arg(desc, arg_out_peers); + command_line::add_arg(desc, arg_tos_flag); + command_line::add_arg(desc, arg_limit_rate_up); + command_line::add_arg(desc, arg_limit_rate_down); + command_line::add_arg(desc, arg_limit_rate); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::init_config() @@ -120,7 +134,6 @@ namespace nodetool //at this moment we have hardcoded config m_config.m_net_config.handshake_interval = P2P_DEFAULT_HANDSHAKE_INTERVAL; - m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; m_config.m_net_config.packet_max_size = P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit m_config.m_net_config.config_id = 0; // initial config m_config.m_net_config.connection_timeout = P2P_DEFAULT_CONNECTION_TIMEOUT; @@ -165,6 +178,7 @@ namespace nodetool m_port = command_line::get_arg(vm, p2p_bind_arg); m_external_port = command_line::get_arg(vm, arg_p2p_external_port); m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); + m_no_igd = command_line::get_arg(vm, arg_no_igd); if (command_line::has_arg(vm, arg_p2p_add_peer)) { @@ -184,11 +198,13 @@ namespace nodetool if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) return false; } + if (command_line::has_arg(vm, arg_p2p_add_priority_node)) { if (!parse_peers_and_add_to_container(vm, arg_p2p_add_priority_node, m_priority_peers)) return false; } + if (command_line::has_arg(vm, arg_p2p_seed_node)) { if (!parse_peers_and_add_to_container(vm, arg_p2p_seed_node, m_seed_nodes)) @@ -197,6 +213,21 @@ namespace nodetool if(command_line::has_arg(vm, arg_p2p_hide_my_port)) m_hide_my_port = true; + + if ( !set_max_out_peers(vm, command_line::get_arg(vm, arg_out_peers) ) ) + return false; + + if ( !set_tos_flag(vm, command_line::get_arg(vm, arg_tos_flag) ) ) + return false; + + if ( !set_rate_up_limit(vm, command_line::get_arg(vm, arg_limit_rate_up) ) ) + return false; + + if ( !set_rate_down_limit(vm, command_line::get_arg(vm, arg_limit_rate_down) ) ) + return false; + + if ( !set_rate_limit(vm, command_line::get_arg(vm, arg_limit_rate) ) ) + return false; return true; } @@ -375,42 +406,43 @@ namespace nodetool LOG_PRINT_L0("External port defined as " << m_external_port); // Add UPnP port mapping - LOG_PRINT_L0("Attempting to add IGD port mapping."); - int result; - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); - UPNPUrls urls; - IGDdatas igdData; - char lanAddress[64]; - result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); - freeUPNPDevlist(deviceList); - if (result != 0) { - if (result == 1) { - std::ostringstream portString; - portString << m_listenning_port; - - // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly - UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0); - - int portMappingResult; - portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0"); - if (portMappingResult != 0) { - LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult)); - } else { - LOG_PRINT_GREEN("Added IGD port mapping.", LOG_LEVEL_0); - } - } else if (result == 2) { - LOG_PRINT_L0("IGD was found but reported as not connected."); - } else if (result == 3) { - LOG_PRINT_L0("UPnP device was found but not recoginzed as IGD."); - } else { - LOG_ERROR("UPNP_GetValidIGD returned an unknown result code."); - } - - FreeUPNPUrls(&urls); - } else { - LOG_PRINT_L0("No IGD was found."); - } - + if(m_no_igd == false) { + LOG_PRINT_L0("Attempting to add IGD port mapping."); + int result; + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); + UPNPUrls urls; + IGDdatas igdData; + char lanAddress[64]; + result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); + freeUPNPDevlist(deviceList); + if (result != 0) { + if (result == 1) { + std::ostringstream portString; + portString << m_listenning_port; + + // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly + UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0); + + int portMappingResult; + portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0"); + if (portMappingResult != 0) { + LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult)); + } else { + LOG_PRINT_GREEN("Added IGD port mapping.", LOG_LEVEL_0); + } + } else if (result == 2) { + LOG_PRINT_L0("IGD was found but reported as not connected."); + } else if (result == 3) { + LOG_PRINT_L0("UPnP device was found but not recoginzed as IGD."); + } else { + LOG_ERROR("UPNP_GetValidIGD returned an unknown result code."); + } + + FreeUPNPUrls(&urls); + } else { + LOG_PRINT_L0("No IGD was found."); + } + } return res; } //----------------------------------------------------------------------------------- @@ -1300,4 +1332,83 @@ namespace nodetool return true; } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max) + { + if(max == -1) { + m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; + return true; + } + + m_config.m_net_config.connections_count = max; + LOG_PRINT_RED_L0("connections_count: " << m_config.m_net_config.connections_count); + return true; + } + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_connections(size_t count) + { + m_net_server.get_config_object().del_connections(count); + } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::set_tos_flag(const boost::program_options::variables_map& vm, int flag) + { + if(flag==-1){ + return true; + } + epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_tos_flag(flag); + _dbg1("Set ToS flag " << flag); + return true; + } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::set_rate_up_limit(const boost::program_options::variables_map& vm, int64_t limit) + { + this->islimitup=true; + + if (limit==-1) { + limit=128; + this->islimitup=false; + } + + limit *= 1024; + epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_up_limit( limit ); + LOG_PRINT_L0("Set limit-up to " << limit/1024 << " kB/s"); + return true; + } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::set_rate_down_limit(const boost::program_options::variables_map& vm, int64_t limit) + { + this->islimitdown=true; + if(limit==-1) { + limit=128; + this->islimitdown=false; + } + limit *= 1024; + epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_down_limit( limit ); + LOG_PRINT_L0("Set limit-down to " << limit/1024 << " kB/s"); + return true; + } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::set_rate_limit(const boost::program_options::variables_map& vm, uint64_t limit) + { + limit *= 1024; + if(this->islimitdown==false && this->islimitup==false) { + epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_up_limit( limit ); + epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_down_limit( limit ); + LOG_PRINT_L0("Set limit to " << limit/1024 << " kB/s"); + } + else if(this->islimitdown==false && this->islimitup==true ) { + epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_down_limit( limit ); + } + else if(this->islimitdown==true && this->islimitup==false ) { + epee::net_utils::connection<epee::levin::async_protocol_handler<p2p_connection_context> >::set_rate_up_limit( limit ); + } + + return true; + } } diff --git a/src/p2p/network_throttle-detail.cpp b/src/p2p/network_throttle-detail.cpp new file mode 100644 index 000000000..6ea3076a9 --- /dev/null +++ b/src/p2p/network_throttle-detail.cpp @@ -0,0 +1,382 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief implementaion for throttling of connection (count and rate-limit speed etc) + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* rfree: implementation for throttle details */ + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <atomic> + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/thread/thread.hpp> + +#include <memory> + +#include "syncobj.h" + +#include "../../contrib/epee/include/net/net_utils_base.h" +#include "../../contrib/epee/include/misc_log_ex.h" +#include <boost/lambda/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/uuid/random_generator.hpp> +#include <boost/chrono.hpp> +#include <boost/utility/value_init.hpp> +#include <boost/asio/deadline_timer.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> +#include "misc_language.h" +#include "pragma_comp_defs.h" +#include <sstream> +#include <iomanip> +#include <algorithm> + + + +#include <boost/asio/basic_socket.hpp> +#include <boost/asio/ip/unicast.hpp> +#include "../../contrib/epee/include/net/abstract_tcp_server2.h" + +// TODO: +#include "../../src/p2p/network_throttle-detail.hpp" + +#include "../../contrib/otshell_utils/utils.hpp" +using namespace nOT::nUtils; + +// ################################################################################################ +// ################################################################################################ +// the "header part". Not separeted out for .hpp because point of this modification is +// to rebuild just 1 translation unit while working on this code. +// (But maybe common parts will be separated out later though - if needed) +// ################################################################################################ +// ################################################################################################ + +using namespace nOT::nUtils; + +namespace epee +{ +namespace net_utils +{ + + +/* ============================================================================ */ + +class connection_basic_pimpl { + public: + connection_basic_pimpl(const std::string &name); + + static int m_default_tos; + + network_throttle_bw m_throttle; // per-perr + critical_section m_throttle_lock; + + void _packet(size_t packet_size, int phase, int q_len); // execute a sleep ; phase is not really used now(?) could be used for different kinds of sleep e.g. direct/queue write +}; + + +} // namespace +} // namespace + + + + + + +// ################################################################################################ +// ################################################################################################ +// The implementation part +// ################################################################################################ +// ################################################################################################ + +namespace epee +{ +namespace net_utils +{ + +// ================================================================================================ +// network_throttle +// ================================================================================================ + +network_throttle::~network_throttle() { } + +network_throttle::packet_info::packet_info() + : m_size(0) +{ +} + +network_throttle::network_throttle(const std::string &nameshort, const std::string &name, int window_size) + : m_window_size( (window_size==-1) ? 10 : window_size ), + m_history( m_window_size ), m_nameshort(nameshort) +{ + set_name(name); + m_network_add_cost = 128; + m_network_minimal_segment = 256; + m_network_max_segment = 1024*1024; + m_any_packet_yet = false; + m_slot_size = 1.0; // hard coded in few places + m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle + m_target_MB = 0; + +} + +void network_throttle::set_name(const std::string &name) +{ + m_name = name; +} + +void network_throttle::set_target_speed( network_speed_kbps target ) +{ + m_target_speed = target; + _note_c("net/"+m_nameshort, "Setting LIMIT: " << target << " kbps"); +} + +void network_throttle::set_target_kill( network_MB target ) +{ + _note_c("net/"+m_nameshort, "Setting KILL: " << target << " MB hard limit"); + m_target_MB = target; +} + + +void network_throttle::tick() +{ + double time_now = get_time_seconds(); + if (!m_any_packet_yet) m_start_time = time_now; // starting now + + network_time_seconds current_sample_time_slot = time_to_slot( time_now ); // T=13.7 --> 13 (for 1-second smallwindow) + network_time_seconds last_sample_time_slot = time_to_slot( m_last_sample_time ); + + // moving to next position, and filling gaps + // !! during this loop the m_last_sample_time and last_sample_time_slot mean the variable moved in +1 + // TODO optimize when moving few slots at once + while ( (!m_any_packet_yet) || (last_sample_time_slot < current_sample_time_slot)) + { + LOG_PRINT_L4("Moving counter buffer by 1 second " << last_sample_time_slot << " < " << current_sample_time_slot << " (last time " << m_last_sample_time<<")"); + // rotate buffer + for (size_t i=m_history.size()-1; i>=1; --i) m_history[i] = m_history[i-1]; + m_history[0] = packet_info(); + if (! m_any_packet_yet) + { + m_last_sample_time = time_now; + } + m_last_sample_time += 1; last_sample_time_slot = time_to_slot( m_last_sample_time ); // increase and recalculate time, time slot + m_any_packet_yet=true; + } + m_last_sample_time = time_now; // the real exact last time +} + +void network_throttle::handle_trafic_exact(size_t packet_size) +{ + _handle_trafic_exact(packet_size, packet_size); +} + +void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_size) +{ + tick(); + + calculate_times_struct cts ; calculate_times(packet_size, cts , false, -1); + calculate_times_struct cts2; calculate_times(packet_size, cts2, false, 5); + m_history[0].m_size += packet_size; + + std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; + std::string history_str = oss.str(); + + logger_handle_net("log/dr-monero/net/inreq-all.data",get_time_seconds(),packet_size); + _info_c( "net/" + m_nameshort , "Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)" + << " Speed AVG=" << std::setw(4) << ((long int)(cts .average/1024)) <<"[w="<<cts .window<<"]" + << " " << std::setw(4) << ((long int)(cts2.average/1024)) <<"[w="<<cts2.window<<"]" + <<" / " << " Limit="<< ((long int)(m_target_speed/1024)) <<" KiB/sec " + << " " << history_str + ); +} + +void network_throttle::handle_trafic_tcp(size_t packet_size) +{ + size_t all_size = packet_size + m_network_add_cost; + all_size = std::max( m_network_minimal_segment , all_size); + _handle_trafic_exact( all_size , packet_size ); +} + +void network_throttle::handle_congestion(double overheat) { + // TODO +} + +network_time_seconds network_throttle::get_sleep_time_after_tick(size_t packet_size) { + tick(); + return get_sleep_time(packet_size); +} + +void network_throttle::logger_handle_net(const std::string &filename, double time, size_t size) { + std::mutex mutex; + mutex.lock(); { + std::fstream file; + file.open(filename.c_str(), std::ios::app | std::ios::out ); + if(!file.is_open()) + _warn("Can't open file " << filename); + file << time << " " << size/1024 << "\n"; + file.close(); + } mutex.unlock(); +} + +// fine tune this to decide about sending speed: +network_time_seconds network_throttle::get_sleep_time(size_t packet_size) const +{ + //_scope_mark(""); + double D2=0; + calculate_times_struct cts = { 0, 0, 0, 0}; + //calculate_times(packet_size, cts, false, m_window_size/2); D2=cts.delay; + //calculate_times(packet_size, cts, true, m_window_size/2); D2=cts.delay; + calculate_times(packet_size, cts, true, m_window_size); D2=cts.delay; + return D2; +} +double network_throttle::get_current_overheat() const { + auto now = get_time_seconds(); + auto diff = now - m_overheat_time; + auto overheat = m_overheat - diff; + overheat = std::max(m_overheat, 0.); + return overheat; +} + +void network_throttle::set_overheat(double lag) { + m_overheat += lag; + m_overheat_time = get_time_seconds(); + LOG_PRINT_L0("Lag: " << lag << ", overheat: " << m_overheat ); +} + +// MAIN LOGIC: +void network_throttle::calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const +{ + const double the_window_size = std::max( (double)m_window_size , + ((force_window>0) ? force_window : m_window_size) + ); + + if (!m_any_packet_yet) { + cts.window=0; cts.average=0; cts.delay=0; + cts.recomendetDataSize = m_network_minimal_segment; // should be overrided by caller anyway + return ; // no packet yet, I can not decide about sleep time + } + + network_time_seconds window_len = (the_window_size-1) * m_slot_size ; // -1 since current slot is not finished + window_len += (m_last_sample_time - time_to_slot(m_last_sample_time)); // add the time for current slot e.g. 13.7-13 = 0.7 + + auto time_passed = get_time_seconds() - m_start_time; + cts.window = std::max( std::min( window_len , time_passed ) , m_slot_size ) ; // window length resulting from size of history but limited by how long ago history was started, + // also at least slot size (e.g. 1 second) to not be ridiculous + // window_len e.g. 5.7 because takes into account current slot time + + size_t Epast = 0; // summ of traffic till now + for (auto sample : m_history) Epast += sample.m_size; + + const size_t E = Epast; + const size_t Enow = Epast + packet_size ; // including the data we're about to send now + + const double M = m_target_speed; // max + const double D1 = (Epast - M*cts.window) / M; // delay - how long to sleep to get back to target speed + const double D2 = (Enow - M*cts.window) / M; // delay - how long to sleep to get back to target speed (including current packet) + + auto O = get_current_overheat(); + auto Ouse = O * 0 ; // XXX TODO + cts.delay = (D1*0.80 + D2*0.20) + Ouse; // finall sleep depends on both with/without current packet + // update_overheat(); + cts.average = Epast/cts.window; // current avg. speed (for info) + + if (Epast <= 0) { + if (cts.delay>=0) cts.delay = 0; // no traffic in history so we will not wait + } + + double Wgood=-1; + { // how much data we recommend now to download + Wgood = the_window_size + 1; + cts.recomendetDataSize = M*cts.window - E; + } + + if (dbg) { + std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; + std::string history_str = oss.str(); + _dbg1_c( "net/"+m_nameshort+"_c" , + "dbg " << m_name << ": " + << "speed is A=" << std::setw(8) <<cts.average<<" vs " + << "Max=" << std::setw(8) <<M<<" " + << " so sleep: " + << "D=" << std::setw(8) <<cts.delay<<" sec " + << "Overheat=" << std::setw(8) <<O<<" sec " + << "E="<< std::setw(8) << E << " (Enow="<<std::setw(8)<<Enow<<") " + << "M=" << std::setw(8) << M <<" W="<< std::setw(8) << cts.window << " " + << "R=" << std::setw(8) << cts.recomendetDataSize << " Wgood" << std::setw(8) << Wgood << " " + << "History: " << std::setw(8) << history_str << " " + << "m_last_sample_time=" << std::setw(8) << m_last_sample_time + ); + + } +} + +double network_throttle::get_time_seconds() const { + using namespace boost::chrono; + auto point = steady_clock::now(); + auto time_from_epoh = point.time_since_epoch(); + auto ms = duration_cast< milliseconds >( time_from_epoh ).count(); + double ms_f = ms; + return ms_f / 1000.; +} + +size_t network_throttle::get_recommended_size_of_planned_transport_window(double force_window) const { + calculate_times_struct cts = { 0, 0, 0, 0}; + network_throttle::calculate_times(0, cts, true, force_window); + cts.recomendetDataSize += m_network_add_cost; + if (cts.recomendetDataSize<0) cts.recomendetDataSize=0; + if (cts.recomendetDataSize>m_network_max_segment) cts.recomendetDataSize=m_network_max_segment; + size_t RI = (long int)cts.recomendetDataSize; + return RI; +} + +size_t network_throttle::get_recommended_size_of_planned_transport() const { + size_t R1=0,R2=0,R3=0; + R1 = get_recommended_size_of_planned_transport_window( -1 ); + R2 = get_recommended_size_of_planned_transport_window( m_window_size/2); + R3 = get_recommended_size_of_planned_transport_window( 8 ); + auto RM = std::min(R1, std::min(R2,R3)); + + const double a1=70, a2=10, a3=10, am=10; // weight of the various windows in decisssion + return (R1*a1 + R2*a2 + R3*a3 + RM*am) / (a1+a2+a3+am); +} + + +} // namespace +} // namespace + diff --git a/src/p2p/network_throttle-detail.hpp b/src/p2p/network_throttle-detail.hpp new file mode 100644 index 000000000..9d492c534 --- /dev/null +++ b/src/p2p/network_throttle-detail.hpp @@ -0,0 +1,133 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief implementaion for throttling of connection (count and rate-limit speed etc) + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +/* rfree: throttle details, implementing rate limiting */ + + +#ifndef INCLUDED_src_p2p_throttle_detail_hpp +#define INCLUDED_src_p2p_throttle_detail_hpp + +#include "../../src/p2p/network_throttle.hpp" + +namespace epee +{ +namespace net_utils +{ + + +class network_throttle : public i_network_throttle { + private: + struct packet_info { + size_t m_size; // octets sent. Summary for given small-window (e.g. for all packaged in 1 second) + packet_info(); + }; + + + network_speed_kbps m_target_speed; + network_MB m_target_MB; + size_t m_network_add_cost; // estimated add cost of headers + size_t m_network_minimal_segment; // estimated minimal cost of sending 1 byte to round up to + size_t m_network_max_segment; // recommended max size of 1 TCP transmission + + const size_t m_window_size; // the number of samples to average over + network_time_seconds m_slot_size; // the size of one slot. TODO: now hardcoded for 1 second e.g. in time_to_slot() + // TODO for big window size, for performance better the substract on change of m_last_sample_time instead of recalculating average of eg >100 elements + + std::vector< packet_info > m_history; // the history of bw usage + network_time_seconds m_last_sample_time; // time of last history[0] - so we know when to rotate the buffer + network_time_seconds m_start_time; // when we were created + bool m_any_packet_yet; // did we yet got any packet to count + + double m_overheat; // last overheat + double m_overheat_time; // time in seconds after epoch + + std::string m_name; // my name for debug and logs + std::string m_nameshort; // my name for debug and logs (used in log file name) + + // each sample is now 1 second + public: + network_throttle(const std::string &nameshort, const std::string &name, int window_size=-1); + virtual ~network_throttle(); + virtual void set_name(const std::string &name); + virtual void set_target_speed( network_speed_kbps target ); + virtual void set_target_kill( network_MB target ); + + // add information about events: + virtual void handle_trafic_exact(size_t packet_size); ///< count the new traffic/packet; the size is exact considering all network costs + virtual void handle_trafic_tcp(size_t packet_size); ///< count the new traffic/packet; the size is as TCP, we will consider MTU etc + virtual void handle_congestion(double overheat); ///< call this when congestion is detected; see example use + + virtual void tick(); ///< poke and update timers/history (recalculates, moves the history if needed, checks the real clock etc) + + virtual double get_time_seconds() const ; ///< timer that we use, time in seconds, monotionic + virtual double get_current_overheat() const; ///< did we detected congestion now. NOT USED NOW TODO + virtual void set_overheat(double lag); ///< did we detected congestion now. NOT USED NOW TODO. rename to add_overheat ? + + // time calculations: + virtual void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const; ///< MAIN LOGIC (see base class for info) + + virtual network_time_seconds get_sleep_time_after_tick(size_t packet_size); ///< increase the timer if needed, and get the package size + virtual network_time_seconds get_sleep_time(size_t packet_size) const; ///< gets the Delay (recommended Delay time) from calc. (not safe: only if time didnt change?) TODO + + virtual size_t get_recommended_size_of_planned_transport() const; ///< what should be the size (bytes) of next data block to be transported + virtual size_t get_recommended_size_of_planned_transport_window(double force_window) const; ///< ditto, but for given windows time frame + //virtual void add_planned_transport(size_t size); + + private: + virtual network_time_seconds time_to_slot(network_time_seconds t) const { return std::floor( t ); } // convert exact time eg 13.7 to rounded time for slot number in history 13 + virtual void _handle_trafic_exact(size_t packet_size, size_t orginal_size); + virtual void logger_handle_net(const std::string &filename, double time, size_t size); +}; + +/*** + * The complete set of traffic throttle for one typical connection +*/ +struct network_throttle_bw { + public: + network_throttle m_in; ///< for incomming traffic (this we can not controll directly as it depends of what others send to us - usually) + network_throttle m_inreq; ///< for requesting incomming traffic (this is exact usually) + network_throttle m_out; ///< for outgoing traffic that we just sent (this is exact usually) + + public: + network_throttle_bw(const std::string &name1); +}; + + + +} // namespace net_utils +} // namespace epee + + +#endif + + diff --git a/src/p2p/network_throttle.cpp b/src/p2p/network_throttle.cpp new file mode 100644 index 000000000..3d5edcdcd --- /dev/null +++ b/src/p2p/network_throttle.cpp @@ -0,0 +1,121 @@ +/** +@file +@author rfree (current maintainer in monero.cc project) +@brief interface for throttling of connection (count and rate-limit speed etc) +@details <PRE> + +Throttling work by: +1) taking note of all traffic (hooks added e.g. to connection class) and measuring speed +2) depending on that information we sleep before sending out data (or send smaller portions of data) +3) depending on the information we can also sleep before sending requests or ask for smaller sets of data to download + +</PRE> + +@image html images/net/rate1-down-1k.png +@image html images/net/rate1-down-full.png +@image html images/net/rate1-up-10k.png +@image html images/net/rate1-up-full.png +@image html images/net/rate2-down-100k.png +@image html images/net/rate2-down-10k.png +@image html images/net/rate2-down-50k.png +@image html images/net/rate2-down-full.png +@image html images/net/rate2-up-100k.png +@image html images/net/rate2-up-10k.png +@image html images/net/rate3-up-10k.png + + +*/ + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "../../src/p2p/network_throttle-detail.hpp" + +namespace epee +{ +namespace net_utils +{ + +// ================================================================================================ +// network_throttle_manager +// ================================================================================================ + +// ================================================================================================ +// static: +std::mutex network_throttle_manager::m_lock_get_global_throttle_in; +std::mutex network_throttle_manager::m_lock_get_global_throttle_inreq; +std::mutex network_throttle_manager::m_lock_get_global_throttle_out; + +int network_throttle_manager::xxx; + + +// ================================================================================================ +// methods: +i_network_throttle & network_throttle_manager::get_global_throttle_in() { + + std::call_once(m_once_get_global_throttle_in, [] { m_obj_get_global_throttle_in.reset(new network_throttle("in/all","<<< global-IN",10)); } ); + return * m_obj_get_global_throttle_in; +} +std::once_flag network_throttle_manager::m_once_get_global_throttle_in; +std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_in; + + + +i_network_throttle & network_throttle_manager::get_global_throttle_inreq() { + std::call_once(m_once_get_global_throttle_inreq, [] { m_obj_get_global_throttle_inreq.reset(new network_throttle("inreq/all", "<== global-IN-REQ",10)); } ); + return * m_obj_get_global_throttle_inreq; +} +std::once_flag network_throttle_manager::m_once_get_global_throttle_inreq; +std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_inreq; + + +i_network_throttle & network_throttle_manager::get_global_throttle_out() { + std::call_once(m_once_get_global_throttle_out, [] { m_obj_get_global_throttle_out.reset(new network_throttle("out/all", ">>> global-OUT",10)); } ); + return * m_obj_get_global_throttle_out; +} +std::once_flag network_throttle_manager::m_once_get_global_throttle_out; +std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_out; + + + + +network_throttle_bw::network_throttle_bw(const std::string &name1) + : m_in("in/"+name1, name1+"-DOWNLOAD"), m_inreq("inreq/"+name1, name1+"-DOWNLOAD-REQUESTS"), m_out("out/"+name1, name1+"-UPLOAD") +{ } + + + + +} // namespace +} // namespace + + + + + diff --git a/src/p2p/network_throttle.hpp b/src/p2p/network_throttle.hpp new file mode 100644 index 000000000..dc25a2c45 --- /dev/null +++ b/src/p2p/network_throttle.hpp @@ -0,0 +1,187 @@ +/// @file +/// @author rfree (current maintainer in monero.cc project) +/// @brief interface for throttling of connection (count and rate-limit speed etc) + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +/* rfree: throttle basic interface */ +/* rfree: also includes the manager for singeton/global such objects */ + + +#ifndef INCLUDED_p2p_network_throttle_hpp +#define INCLUDED_p2p_network_throttle_hpp + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <atomic> + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/thread/thread.hpp> + +#include "syncobj.h" + +#include "../../contrib/epee/include/net/net_utils_base.h" +#include "../../contrib/epee/include/misc_log_ex.h" +#include <boost/lambda/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/uuid/random_generator.hpp> +#include <boost/chrono.hpp> +#include <boost/utility/value_init.hpp> +#include <boost/asio/deadline_timer.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> +#include "misc_language.h" +#include "pragma_comp_defs.h" +#include <sstream> +#include <iomanip> +#include <algorithm> + +#include <memory> +#include <mutex> +#include <fstream> + +namespace epee +{ +namespace net_utils +{ + +// just typedefs to in code define the units used. TODO later it will be enforced that casts to other numericals are only explicit to avoid mistakes? use boost::chrono? +typedef double network_speed_kbps; +typedef double network_time_seconds; +typedef double network_MB; + +class i_network_throttle; + +/*** +@brief All information about given throttle - speed calculations +*/ +struct calculate_times_struct { + double average; + double window; + double delay; + double recomendetDataSize; +}; +typedef calculate_times_struct calculate_times_struct; + + +namespace cryptonote { class cryptonote_protocol_handler_base; }; // a friend class // TODO friend not working + +/*** +@brief Access to simple throttles, with singlton to access global network limits +*/ +class network_throttle_manager { + // provides global (singleton) in/inreq/out throttle access + + // [[note1]] see also http://www.nuonsoft.com/blog/2012/10/21/implementing-a-thread-safe-singleton-with-c11/ + // [[note2]] _inreq is the requested in traffic - we anticipate we will get in-bound traffic soon as result of what we do (e.g. that we sent network downloads requests) + + //protected: + public: // XXX + // [[note1]] + static std::once_flag m_once_get_global_throttle_in; + static std::once_flag m_once_get_global_throttle_inreq; // [[note2]] + static std::once_flag m_once_get_global_throttle_out; + static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_in; + static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_inreq; + static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_out; + + static std::mutex m_lock_get_global_throttle_in; + static std::mutex m_lock_get_global_throttle_inreq; + static std::mutex m_lock_get_global_throttle_out; + + friend class cryptonote::cryptonote_protocol_handler_base; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS! + friend class connection_basic; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS! + friend class connection_basic_pimpl; // ditto + + static int xxx; + + public: + static i_network_throttle & get_global_throttle_in(); ///< singleton ; for friend class ; caller MUST use proper locks! like m_lock_get_global_throttle_in + static i_network_throttle & get_global_throttle_inreq(); ///< ditto ; use lock ... use m_lock_get_global_throttle_inreq obviously + static i_network_throttle & get_global_throttle_out(); ///< ditto ; use lock ... use m_lock_get_global_throttle_out obviously +}; + + + +/*** +@brief interface for the throttle, see the derivated class +*/ +class i_network_throttle { + public: + virtual void set_name(const std::string &name)=0; + virtual void set_target_speed( network_speed_kbps target )=0; + virtual void set_target_kill( network_MB target )=0; + + virtual void handle_trafic_exact(size_t packet_size) =0; // count the new traffic/packet; the size is exact considering all network costs + virtual void handle_trafic_tcp(size_t packet_size) =0; // count the new traffic/packet; the size is as TCP, we will consider MTU etc + virtual void handle_congestion(double overheat) =0; // call this when congestion is detected; see example use + virtual void tick() =0; // poke and update timers/history + + // time calculations: + + virtual void calculate_times(size_t packet_size, calculate_times_struct &cts, bool dbg, double force_window) const =0; // assuming sending new package (or 0), calculate: + // Average, Window, Delay, Recommended data size ; also gets dbg=debug flag, and forced widnow size if >0 or -1 for not forcing window size + + // Average speed, Window size, recommended Delay to sleep now, Recommended size of data to send now + + virtual network_time_seconds get_sleep_time(size_t packet_size) const =0; // gets the D (recommended Delay time) from calc + virtual network_time_seconds get_sleep_time_after_tick(size_t packet_size) =0; // ditto, but first tick the timer + + virtual size_t get_recommended_size_of_planned_transport() const =0; // what should be the recommended limit of data size that we can transport over current network_throttle in near future + + virtual double get_time_seconds() const =0; // a timer + virtual double get_current_overheat() const =0; + virtual void set_overheat(double lag) =0; + virtual void logger_handle_net(const std::string &filename, double time, size_t size)=0; + + +}; + + +// ... more in the -advanced.h file + + +} // namespace net_utils +} // namespace epee + + +#endif + + + diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 84acbf295..a33ed0f32 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -50,6 +50,7 @@ target_link_libraries(simplewallet crypto common mnemonics + p2p ${UNBOUND_LIBRARY} ${UPNP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} |