Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow blocking whole subnets #5363

Merged
merged 2 commits into from
Jul 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contrib/epee/include/net/abstract_tcp_server2.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ namespace net_utils

struct i_connection_filter
{
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address)=0;
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL)=0;
protected:
virtual ~i_connection_filter(){}
};
Expand Down
49 changes: 48 additions & 1 deletion contrib/epee/include/net/net_utils_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#define MONERO_DEFAULT_LOG_CATEGORY "net"

#ifndef MAKE_IP
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(((uint32_t)a4)<<24))
#endif

#if BOOST_VERSION >= 107000
Expand Down Expand Up @@ -107,6 +107,53 @@ namespace net_utils
inline bool operator>=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
{ return !lhs.less(rhs); }

class ipv4_network_subnet
{
uint32_t m_ip;
uint8_t m_mask;

public:
constexpr ipv4_network_subnet() noexcept
: ipv4_network_subnet(0, 0)
{}

constexpr ipv4_network_subnet(uint32_t ip, uint8_t mask) noexcept
: m_ip(ip), m_mask(mask) {}

bool equal(const ipv4_network_subnet& other) const noexcept;
bool less(const ipv4_network_subnet& other) const noexcept;
constexpr bool is_same_host(const ipv4_network_subnet& other) const noexcept
{ return subnet() == other.subnet(); }
bool matches(const ipv4_network_address &address) const;

constexpr uint32_t subnet() const noexcept { return m_ip & ~(0xffffffffull << m_mask); }
std::string str() const;
std::string host_str() const;
bool is_loopback() const;
bool is_local() const;
static constexpr address_type get_type_id() noexcept { return address_type::invalid; }
static constexpr zone get_zone() noexcept { return zone::public_; }
static constexpr bool is_blockable() noexcept { return true; }

BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_ip)
KV_SERIALIZE(m_mask)
END_KV_SERIALIZE_MAP()
};

inline bool operator==(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this replacing instead of adding the functions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

er, this was not intended, I'll fix.

{ return lhs.equal(rhs); }
inline bool operator!=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
{ return !lhs.equal(rhs); }
inline bool operator<(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
{ return lhs.less(rhs); }
inline bool operator<=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
{ return !rhs.less(lhs); }
inline bool operator>(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
{ return rhs.less(lhs); }
inline bool operator>=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
{ return !lhs.less(rhs); }

class network_address
{
struct interface
Expand Down
18 changes: 18 additions & 0 deletions contrib/epee/src/net_utils_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ namespace epee { namespace net_utils
bool ipv4_network_address::is_local() const { return net_utils::is_ip_local(ip()); }


bool ipv4_network_subnet::equal(const ipv4_network_subnet& other) const noexcept
{ return is_same_host(other) && m_mask == other.m_mask; }

bool ipv4_network_subnet::less(const ipv4_network_subnet& other) const noexcept
{ return subnet() < other.subnet() ? true : (other.subnet() < subnet() ? false : (m_mask < other.m_mask)); }

std::string ipv4_network_subnet::str() const
{ return string_tools::get_ip_string_from_int32(subnet()) + "/" + std::to_string(m_mask); }

std::string ipv4_network_subnet::host_str() const { return string_tools::get_ip_string_from_int32(subnet()) + "/" + std::to_string(m_mask); }
bool ipv4_network_subnet::is_loopback() const { return net_utils::is_ip_loopback(subnet()); }
bool ipv4_network_subnet::is_local() const { return net_utils::is_ip_local(subnet()); }
bool ipv4_network_subnet::matches(const ipv4_network_address &address) const
{
return (address.ip() & ~(0xffffffffull << m_mask)) == subnet();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is broken on big endian machines.

}


bool network_address::equal(const network_address& other) const
{
// clang typeid workaround
Expand Down
7 changes: 7 additions & 0 deletions src/daemon/command_parser_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,13 @@ bool t_command_parser_executor::unban(const std::vector<std::string>& args)
return m_executor.unban(ip);
}

bool t_command_parser_executor::banned(const std::vector<std::string>& args)
{
if (args.size() != 1) return false;
std::string address = args[0];
return m_executor.banned(address);
}

bool t_command_parser_executor::flush_txpool(const std::vector<std::string>& args)
{
if (args.size() > 1) return false;
Expand Down
2 changes: 2 additions & 0 deletions src/daemon/command_parser_executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ class t_command_parser_executor final

bool unban(const std::vector<std::string>& args);

bool banned(const std::vector<std::string>& args);

bool flush_txpool(const std::vector<std::string>& args);

bool output_histogram(const std::vector<std::string>& args);
Expand Down
8 changes: 7 additions & 1 deletion src/daemon/command_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,15 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"unban"
, std::bind(&t_command_parser_executor::unban, &m_parser, p::_1)
, "unban <IP>"
, "unban <address>"
, "Unban a given <IP>."
);
m_command_lookup.set_handler(
"banned"
, std::bind(&t_command_parser_executor::banned, &m_parser, p::_1)
, "banned <address>"
, "Check whether an <address> is banned."
);
m_command_lookup.set_handler(
"flush_txpool"
, std::bind(&t_command_parser_executor::flush_txpool, &m_parser, p::_1)
Expand Down
53 changes: 40 additions & 13 deletions src/daemon/rpc_command_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1641,26 +1641,23 @@ bool t_rpc_command_executor::print_bans()

for (auto i = res.bans.begin(); i != res.bans.end(); ++i)
{
tools::msg_writer() << epee::string_tools::get_ip_string_from_int32(i->ip) << " banned for " << i->seconds << " seconds";
tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds";
}

return true;
}


bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
bool t_rpc_command_executor::ban(const std::string &address, time_t seconds)
{
cryptonote::COMMAND_RPC_SETBANS::request req;
cryptonote::COMMAND_RPC_SETBANS::response res;
std::string fail_message = "Unsuccessful";
epee::json_rpc::error error_resp;

cryptonote::COMMAND_RPC_SETBANS::ban ban;
if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip))
{
tools::fail_msg_writer() << "Invalid IP";
return true;
}
ban.host = address;
ban.ip = 0;
ban.ban = true;
ban.seconds = seconds;
req.bans.push_back(ban);
Expand All @@ -1684,19 +1681,16 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
return true;
}

bool t_rpc_command_executor::unban(const std::string &ip)
bool t_rpc_command_executor::unban(const std::string &address)
{
cryptonote::COMMAND_RPC_SETBANS::request req;
cryptonote::COMMAND_RPC_SETBANS::response res;
std::string fail_message = "Unsuccessful";
epee::json_rpc::error error_resp;

cryptonote::COMMAND_RPC_SETBANS::ban ban;
if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip))
{
tools::fail_msg_writer() << "Invalid IP";
return true;
}
ban.host = address;
ban.ip = 0;
ban.ban = false;
ban.seconds = 0;
req.bans.push_back(ban);
Expand All @@ -1720,6 +1714,39 @@ bool t_rpc_command_executor::unban(const std::string &ip)
return true;
}

bool t_rpc_command_executor::banned(const std::string &address)
{
cryptonote::COMMAND_RPC_BANNED::request req;
cryptonote::COMMAND_RPC_BANNED::response res;
std::string fail_message = "Unsuccessful";
epee::json_rpc::error error_resp;

req.address = address;

if (m_is_rpc)
{
if (!m_rpc_client->json_rpc_request(req, res, "banned", fail_message.c_str()))
{
return true;
}
}
else
{
if (!m_rpc_server->on_banned(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}

if (res.banned)
tools::msg_writer() << address << " is banned for " << res.seconds << " seconds";
else
tools::msg_writer() << address << " is not banned";

return true;
}

bool t_rpc_command_executor::flush_txpool(const std::string &txid)
{
cryptonote::COMMAND_RPC_FLUSH_TRANSACTION_POOL::request req;
Expand Down
6 changes: 4 additions & 2 deletions src/daemon/rpc_command_executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,11 @@ class t_rpc_command_executor final {

bool print_bans();

bool ban(const std::string &ip, time_t seconds);
bool ban(const std::string &address, time_t seconds);

bool unban(const std::string &ip);
bool unban(const std::string &address);

bool banned(const std::string &address);

bool flush_txpool(const std::string &txid);

Expand Down
3 changes: 2 additions & 1 deletion src/net/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ namespace net
invalid_i2p_address,
invalid_port, //!< Outside of 0-65535 range
invalid_tor_address,//!< Invalid base32 or length
unsupported_address //!< Type not supported by `get_network_address`
unsupported_address,//!< Type not supported by `get_network_address`
invalid_mask, //!< Outside of 0-32 range
};

//! \return `std::error_category` for `net` namespace.
Expand Down
23 changes: 23 additions & 0 deletions src/net/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,27 @@ namespace net
return {epee::net_utils::ipv4_network_address{ip, port}};
return make_error_code(net::error::unsupported_address);
}

expect<epee::net_utils::ipv4_network_subnet>
get_ipv4_subnet_address(const boost::string_ref address, bool allow_implicit_32)
{
uint32_t mask = 32;
const boost::string_ref::size_type slash = address.find_first_of('/');
if (slash != boost::string_ref::npos)
{
if (!epee::string_tools::get_xtype_from_string(mask, std::string{address.substr(slash + 1)}))
return make_error_code(net::error::invalid_mask);
if (mask > 32)
return make_error_code(net::error::invalid_mask);
}
else if (!allow_implicit_32)
return make_error_code(net::error::invalid_mask);

std::uint32_t ip = 0;
boost::string_ref S(address.data(), slash != boost::string_ref::npos ? slash : address.size());
if (!epee::string_tools::get_ip_int32_from_string(ip, std::string(S)))
return make_error_code(net::error::invalid_host);

return {epee::net_utils::ipv4_network_subnet{ip, (uint8_t)mask}};
}
}
13 changes: 13 additions & 0 deletions src/net/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,18 @@ namespace net
*/
expect<epee::net_utils::network_address>
get_network_address(boost::string_ref address, std::uint16_t default_port);

/*!
Identifies an IPv4 subnet in CIDR notatioa and returns it as a generic
`network_address`. If the type is unsupported, it might be a hostname,
and `error() == net::error::kUnsupportedAddress` is returned.

\param address An ipv4 address.
\param allow_implicit_32 whether to accept "raw" IPv4 addresses, with CIDR notation

\return A tor or IPv4 address, else error.
*/
expect<epee::net_utils::ipv4_network_subnet>
get_ipv4_subnet_address(boost::string_ref address, bool allow_implicit_32 = false);
}

13 changes: 9 additions & 4 deletions src/p2p/net_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,11 @@ namespace nodetool
void change_max_in_public_peers(size_t count);
virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_host(const epee::net_utils::network_address &address);
virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet);
virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return !is_remote_host_allowed(address, seconds); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have this alias? It does not appear to be overriding any base class implementation either.

Copy link
Collaborator Author

@moneromooo-monero moneromooo-monero Apr 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's a public entry point for this, which just piggy backs on the base class stuff, and its override is private below.

virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_subnets; }

virtual void add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
virtual void remove_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
Expand Down Expand Up @@ -319,7 +323,7 @@ namespace nodetool
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f);
virtual bool add_host_fail(const epee::net_utils::network_address &address);
//----------------- i_connection_filter --------------------------------------------------------
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address);
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL);
//-----------------------------------------------------------------------------------------------
bool parse_peer_from_string(epee::net_utils::network_address& pe, const std::string& node_addr, uint16_t default_port = 0);
bool handle_command_line(
Expand Down Expand Up @@ -461,8 +465,9 @@ namespace nodetool
std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache;
epee::critical_section m_conn_fails_cache_lock;

epee::critical_section m_blocked_hosts_lock;
std::map<std::string, time_t> m_blocked_hosts;
epee::critical_section m_blocked_hosts_lock; // for both hosts and subnets
std::map<epee::net_utils::network_address, time_t> m_blocked_hosts;
std::map<epee::net_utils::ipv4_network_subnet, time_t> m_blocked_subnets;

epee::critical_section m_host_fails_score_lock;
std::map<std::string, uint64_t> m_host_fails_score;
Expand Down
Loading