more robust mechanism to determine external IP

This commit is contained in:
Arvid Norberg 2010-12-24 01:31:41 +00:00
parent 8cec51fc47
commit 451c583023
16 changed files with 360 additions and 39 deletions

View File

@ -9,6 +9,7 @@ set(sources
bandwidth_limit
bandwidth_manager
bandwidth_queue_entry
bloom_filter
connection_queue
create_torrent
disk_buffer_holder

View File

@ -368,6 +368,7 @@ SOURCES =
bandwidth_limit
bandwidth_manager
bandwidth_queue_entry
bloom_filter
connection_queue
create_torrent
disk_buffer_holder

View File

@ -18,6 +18,7 @@ nobase_include_HEADERS = \
bandwidth_queue_entry.hpp \
bencode.hpp \
bitfield.hpp \
bloom_filter.hpp \
broadcast_socket.hpp \
bt_peer_connection.hpp \
buffer.hpp \

View File

@ -83,6 +83,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socket_io.hpp" // for print_address
#include "libtorrent/address.hpp"
#include "libtorrent/utp_socket_manager.hpp"
#include "libtorrent/bloom_filter.hpp"
#ifdef TORRENT_STATS
#include <fstream>
@ -393,7 +394,16 @@ namespace libtorrent
char* allocate_disk_buffer(char const* category);
void free_disk_buffer(char* buf);
void set_external_address(address const& ip);
enum
{
source_dht = 1,
source_peer = 2,
source_tracker = 4,
source_router = 8
};
void set_external_address(address const& ip
, int source_type, address const& source);
address const& external_address() const { return m_external_address; }
// used when posting synchronous function
@ -834,6 +844,34 @@ namespace libtorrent
#ifdef TORRENT_UPNP_LOGGING
std::ofstream m_upnp_log;
#endif
struct external_ip_t
{
external_ip_t(): sources(0), num_votes(0) {}
bool add_vote(sha1_hash const& k, int type);
bool operator<(external_ip_t const& rhs) const
{
if (num_votes < rhs.num_votes) return true;
if (rhs.num_votes > num_votes) return false;
return sources < rhs.sources;
}
// this is a bloom filter of the IPs that have
// reported this address
bloom_filter<16> voters;
// this is the actual external address
address addr;
// a bitmask of sources the reporters have come from
boost::uint16_t sources;
// the total number of votes for this IP
boost::uint16_t num_votes;
};
// this is a bloom filter of all the IPs that have
// been the first to report an external address. Each
// IP only gets to add a new item once.
bloom_filter<32> m_external_address_voters;
std::vector<external_ip_t> m_external_addresses;
address m_external_address;
#ifndef TORRENT_DISABLE_EXTENSIONS

View File

@ -0,0 +1,72 @@
/*
Copyright (c) 2010, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of the author 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 OWNER 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.
*/
#ifndef TORRENT_BLOOM_FILTER_HPP_INCLUDED
#define TORRENT_BLOOM_FILTER_HPP_INCLUDED
#include <boost/cstdint.hpp>
#include "libtorrent/peer_id.hpp" // for sha1_hash
namespace libtorrent
{
void set_bit(boost::uint32_t b, boost::uint8_t* bits, int len);
bool has_bit(boost::uint32_t b, boost::uint8_t const* bits, int len);
template <int N>
struct bloom_filter
{
bool find(sha1_hash const& k) const
{
return has_bit(k[0], bits, N)
&& has_bit(k[1], bits, N)
&& has_bit(k[2], bits, N);
}
void set(sha1_hash const& k)
{
set_bit(k[0], bits, N);
set_bit(k[1], bits, N);
set_bit(k[2], bits, N);
}
void clear() { memset(bits, 0, N); }
bloom_filter() { clear(); }
private:
boost::uint8_t bits[N];
};
}
#endif

View File

@ -124,6 +124,12 @@ namespace libtorrent
static mutex file_mutex;
#endif
~logger()
{
mutex::scoped_lock l(file_mutex);
log_file.close();
}
logger(std::string const& logpath, std::string const& filename
, int instance, bool append)
{

View File

@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/io.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/lazy_entry.hpp"
#include "libtorrent/peer_id.hpp" // for sha1_hash
#include <string>
namespace libtorrent
@ -46,6 +47,7 @@ namespace libtorrent
TORRENT_EXPORT std::string print_endpoint(tcp::endpoint const& ep);
TORRENT_EXPORT std::string print_endpoint(udp::endpoint const& ep);
TORRENT_EXPORT std::string address_to_bytes(address const& a);
TORRENT_EXPORT void hash_address(address const& ip, sha1_hash& h);
namespace detail
{

View File

@ -24,6 +24,7 @@ libtorrent_rasterbar_la_SOURCES = \
bandwidth_limit.cpp \
bandwidth_manager.cpp \
bandwidth_queue_entry.cpp \
bloom_filter.cpp \
broadcast_socket.cpp \
bt_peer_connection.cpp \
connection_queue.cpp \

43
src/bloom_filter.cpp Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright (c) 2010, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of the author 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 OWNER 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 "libtorrent/bloom_filter.hpp"
namespace libtorrent
{
bool has_bit(boost::uint32_t b, boost::uint8_t const* bits, int len)
{ b %= len * 8; return bits[b/8] & (1 << (b & 7)); }
void set_bit(boost::uint32_t b, boost::uint8_t* bits, int len)
{ b %= len * 8; bits[b/8] |= (1 << (b & 7)); }
}

View File

@ -1788,7 +1788,8 @@ namespace libtorrent
{
address_v4::bytes_type bytes;
std::copy(myip.begin(), myip.end(), bytes.begin());
m_ses.set_external_address(address_v4(bytes));
m_ses.set_external_address(address_v4(bytes)
, aux::session_impl::source_peer, remote().address());
}
#if TORRENT_USE_IPV6
else if (myip.size() == address_v6::bytes_type::static_size)
@ -1797,9 +1798,11 @@ namespace libtorrent
std::copy(myip.begin(), myip.end(), bytes.begin());
address_v6 ipv6_address(bytes);
if (ipv6_address.is_v4_mapped())
m_ses.set_external_address(ipv6_address.to_v4());
m_ses.set_external_address(ipv6_address.to_v4()
, aux::session_impl::source_peer, remote().address());
else
m_ses.set_external_address(ipv6_address);
m_ses.set_external_address(ipv6_address
, aux::session_impl::source_peer, remote().address());
}
#endif
}

View File

@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/hasher.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_local et.al
#include "libtorrent/socket_io.hpp" // for hash_address
namespace libtorrent { namespace dht
{
@ -96,22 +97,6 @@ int distance_exp(node_id const& n1, node_id const& n2)
struct static_ { static_() { std::srand(std::time(0)); } } static__;
void hash_address(address const& ip, sha1_hash& h)
{
#if TORRENT_USE_IPV6
if (ip.is_v6())
{
address_v6::bytes_type b = ip.to_v6().to_bytes();
h = hasher((char*)&b[0], b.size()).final();
}
else
#endif
{
address_v4::bytes_type b = ip.to_v4().to_bytes();
h = hasher((char*)&b[0], b.size()).final();
}
}
// verifies whether a node-id matches the IP it's used from
// returns true if the node-id is OK coming from this source
// and false otherwise.

View File

@ -346,7 +346,7 @@ bool rpc_manager::incoming(msg const& m, node_id* id)
// this node claims we use the wrong node-ID!
address_v4::bytes_type b;
memcpy(&b[0], ext_ip->string_ptr(), 4);
m_ses.set_external_address(address_v4(b));
m_ses.set_external_address(address_v4(b), aux::session_impl::source_dht, m.addr.address());
}
#if TORRENT_USE_IPV6
else if (ext_ip && ext_ip->string_length() == 16)
@ -354,7 +354,7 @@ bool rpc_manager::incoming(msg const& m, node_id* id)
// this node claims we use the wrong node-ID!
address_v6::bytes_type b;
memcpy(&b[0], ext_ip->string_ptr(), 16);
m_ses.set_external_address(address_v6(b));
m_ses.set_external_address(address_v6(b), aux::session_impl::source_dht, m.addr.address());
}
#endif

View File

@ -900,11 +900,6 @@ namespace aux {
save_struct(e[c.name], reinterpret_cast<char const*>(this) + c.offset
, c.map, c.num_entries, reinterpret_cast<char const*>(&def) + c.default_offset);
}
#ifndef TORRENT_DISABLE_DHT
if (flags & session::save_dht_settings)
{
}
#endif
#ifndef TORRENT_DISABLE_DHT
if (m_dht && (flags & session::save_dht_state))
{
@ -3720,7 +3715,9 @@ namespace aux {
if (mapping == m_tcp_mapping[map_transport] && port != 0)
{
if (ip != address()) set_external_address(ip);
// TODO: report the proper address of the router
if (ip != address()) set_external_address(ip, source_router
, address());
if (!m_listen_sockets.empty()) {
m_listen_sockets.front().external_address = ip;
@ -3994,10 +3991,6 @@ namespace aux {
session_impl::~session_impl()
{
#if defined BOOST_HAS_PTHREADS
TORRENT_ASSERT(!is_network_thread());
#endif
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n";
#endif
@ -4026,7 +4019,7 @@ namespace aux {
printf("\n==== Waiting to shut down: %d ==== \n\n", counter);
}
#endif
m_thread->join();
if (m_thread) m_thread->join();
TORRENT_ASSERT(m_torrents.empty());
TORRENT_ASSERT(m_connections.empty());
@ -4367,18 +4360,95 @@ namespace aux {
m_upnp = 0;
}
void session_impl::set_external_address(address const& ip)
bool session_impl::external_ip_t::add_vote(sha1_hash const& k, int type)
{
sources |= type;
if (voters.find(k)) return false;
voters.set(k);
++num_votes;
return true;
}
void session_impl::set_external_address(address const& ip
, int source_type, address const& source)
{
TORRENT_ASSERT(ip != address());
if (is_local(ip)) return;
if (is_loopback(ip)) return;
if (m_external_address == ip) return;
// for now, just trust whoever tells us our external address first
if (m_external_address != address()) return;
#if defined TORRENT_VERBOSE_LOGGING
(*m_logger) << time_now_string() << ": set_external_address(" << print_address(ip)
<< ", " << source_type << ", " << print_address(source) << ")\n";
#endif
// this is the key to use for the bloom filters
// it represents the identity of the voter
sha1_hash k;
hash_address(source, k);
// do we already have an entry for this external IP?
std::vector<external_ip_t>::iterator i = std::find_if(m_external_addresses.begin()
, m_external_addresses.end(), boost::bind(&external_ip_t::addr, _1) == ip);
if (i == m_external_addresses.end())
{
// each IP only gets to add a new IP once
if (m_external_address_voters.find(k)) return;
if (m_external_addresses.size() > 20)
{
if (rand() < RAND_MAX / 2)
{
#if defined TORRENT_VERBOSE_LOGGING
(*m_logger) << time_now_string() << ": More than 20 slots, dopped\n";
#endif
return;
}
// use stable sort here to maintain the fifo-order
// of the entries with the same number of votes
// this will sort in ascending order, i.e. the lowest
// votes first. Also, the oldest are first, so this
// is a sort of weighted LRU.
std::stable_sort(m_external_addresses.begin(), m_external_addresses.end());
// erase the first element, since this is the
// oldest entry and the one with lowst number
// of votes. This makes sense because the oldest
// entry has had the longest time to receive more
// votes to be bumped up
#if defined TORRENT_VERBOSE_LOGGING
(*m_logger) << " More than 20 slots, dopping "
<< print_address(m_external_addresses.front().addr)
<< " (" << m_external_addresses.front().num_votes << ")\n";
#endif
m_external_addresses.erase(m_external_addresses.begin());
}
m_external_addresses.push_back(external_ip_t());
i = m_external_addresses.end() - 1;
i->addr = ip;
}
// add one more vote to this external IP
if (!i->add_vote(k, source_type)) return;
i = std::max_element(m_external_addresses.begin(), m_external_addresses.end());
TORRENT_ASSERT(i != m_external_addresses.end());
#if defined TORRENT_VERBOSE_LOGGING
for (std::vector<external_ip_t>::iterator j = m_external_addresses.begin()
, end(m_external_addresses.end()); j != end; ++j)
{
(*m_logger) << ((j == i)?"-->":" ")
<< print_address(j->addr) << " votes: "
<< j->num_votes << "\n";
}
#endif
if (i->addr == m_external_address) return;
#if defined TORRENT_VERBOSE_LOGGING
(*m_logger) << " external IP updated\n";
#endif
m_external_address = i->addr;
m_external_address_voters.clear();
m_external_address = ip;
if (m_alerts.should_post<external_ip_alert>())
m_alerts.post_alert(external_ip_alert(ip));

View File

@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socket.hpp"
#include "libtorrent/socket_io.hpp"
#include "libtorrent/address.hpp"
#include "libtorrent/hasher.hpp" // for hasher
namespace libtorrent
{
@ -92,5 +93,21 @@ namespace libtorrent
return print_endpoint(tcp::endpoint(ep.address(), ep.port()));
}
void hash_address(address const& ip, sha1_hash& h)
{
#if TORRENT_USE_IPV6
if (ip.is_v6())
{
address_v6::bytes_type b = ip.to_v6().to_bytes();
h = hasher((char*)&b[0], b.size()).final();
}
else
#endif
{
address_v4::bytes_type b = ip.to_v4().to_bytes();
h = hasher((char*)&b[0], b.size()).final();
}
}
}

View File

@ -1743,8 +1743,10 @@ namespace libtorrent
INVARIANT_CHECK;
TORRENT_ASSERT(r.kind == tracker_request::announce_request);
TORRENT_ASSERT(!tracker_ips.empty());
if (external_ip != address())
m_ses.set_external_address(external_ip);
m_ses.set_external_address(external_ip, aux::session_impl::source_tracker
, *tracker_ips.begin());
ptime now = time_now();

View File

@ -48,6 +48,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/bencode.hpp"
#include "libtorrent/timestamp_history.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/bloom_filter.hpp"
#include "libtorrent/aux_/session_impl.hpp"
#ifndef TORRENT_DISABLE_DHT
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/routing_table.hpp"
@ -379,12 +381,89 @@ namespace libtorrent
TORRENT_EXPORT void find_control_url(int type, char const* string, parse_state& state);
address rand_v4()
{
return address_v4(rand() << 16 | rand());
}
int test_main()
{
using namespace libtorrent;
error_code ec;
int ret = 0;
// test external ip voting
aux::session_impl* ses = new aux::session_impl(std::pair<int, int>(0,0)
, fingerprint("LT", 0, 0, 0, 0), "0.0.0.0"
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
, ""
#endif
);
// test a single malicious node
// adds 50 legitimate responses from different peers
// and 50 malicious responses from the same peer
address real_external = address_v4::from_string("5.5.5.5");
address malicious = address_v4::from_string("4.4.4.4");
for (int i = 0; i < 50; ++i)
{
ses->set_external_address(real_external, aux::session_impl::source_dht, rand_v4());
ses->set_external_address(rand_v4(), aux::session_impl::source_dht, malicious);
}
TEST_CHECK(ses->external_address() == real_external);
ses->abort();
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
ses->m_logger.reset();
#endif
delete ses;
ses = new aux::session_impl(std::pair<int, int>(0,0)
, fingerprint("LT", 0, 0, 0, 0), "0.0.0.0"
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
, ""
#endif
);
// test a single malicious node
// adds 50 legitimate responses from different peers
// and 50 consistent malicious responses from the same peer
real_external = address_v4::from_string("5.5.5.5");
malicious = address_v4::from_string("4.4.4.4");
address malicious_external = address_v4::from_string("3.3.3.3");
for (int i = 0; i < 50; ++i)
{
ses->set_external_address(real_external, aux::session_impl::source_dht, rand_v4());
ses->set_external_address(malicious_external, aux::session_impl::source_dht, malicious);
}
TEST_CHECK(ses->external_address() == real_external);
ses->abort();
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
ses->m_logger.reset();
#endif
delete ses;
// test bloom_filter
bloom_filter<32> filter;
sha1_hash k1 = hasher("test1", 5).final();
sha1_hash k2 = hasher("test2", 5).final();
sha1_hash k3 = hasher("test3", 5).final();
sha1_hash k4 = hasher("test4", 5).final();
TEST_CHECK(!filter.find(k1));
TEST_CHECK(!filter.find(k2));
TEST_CHECK(!filter.find(k3));
TEST_CHECK(!filter.find(k4));
filter.set(k1);
TEST_CHECK(filter.find(k1));
TEST_CHECK(!filter.find(k2));
TEST_CHECK(!filter.find(k3));
TEST_CHECK(!filter.find(k4));
filter.set(k4);
TEST_CHECK(filter.find(k1));
TEST_CHECK(!filter.find(k2));
TEST_CHECK(!filter.find(k3));
TEST_CHECK(filter.find(k4));
// test timestamp_history
{
timestamp_history h;