Add dual stack DHT node simulation tests

This commit is contained in:
Steven Siloti 2016-01-10 11:25:44 -08:00
parent 2abd9867ce
commit 43be11177b
5 changed files with 270 additions and 44 deletions

View File

@ -64,6 +64,13 @@ namespace {
return asio::ip::address_v4(lt::random());
}
asio::ip::address addr6_from_int(int idx)
{
asio::ip::address_v6::bytes_type bytes;
for (uint8_t& b : bytes) b = uint8_t(lt::random());
return asio::ip::address_v6(bytes);
}
// this is the node ID assigned to node 'idx'
dht::node_id id_from_addr(lt::address const& addr)
{
@ -74,28 +81,27 @@ namespace {
struct dht_node final : lt::dht::udp_socket_interface
{
enum flags_t
{
add_dead_nodes = 1
};
dht_node(sim::simulation& sim, lt::dht_settings const& sett, lt::counters& cnt
, int idx, std::uint32_t flags)
: m_io_service(sim, addr_from_int(idx))
: m_io_service(sim, (flags & dht_network::bind_ipv6) ? addr6_from_int(idx) : addr_from_int(idx))
#if LIBSIMULATOR_USE_MOVE
, m_socket(m_io_service)
, m_dht(ipv4, this, sett, id_from_addr(m_io_service.get_ips().front())
, m_dht((flags & dht_network::bind_ipv6) ? ipv6 : ipv4
, this, sett, id_from_addr(m_io_service.get_ips().front())
, nullptr, cnt, std::map<std::string, lt::dht::node*>())
#else
, m_socket(new asio::ip::udp::socket(m_io_service))
, m_dht(new lt::dht::node(ipv4, this, sett, id_from_addr(m_io_service.get_ips().front())
, m_dht(new lt::dht::node((flags & dht_network::bind_ipv6) ? ipv6 : ipv4
, this, sett, id_from_addr(m_io_service.get_ips().front())
, nullptr, cnt, std::map<std::string, lt::dht::node*>()))
#endif
, m_add_dead_nodes(flags & add_dead_nodes)
, m_add_dead_nodes(flags & dht_network::add_dead_nodes)
, m_ipv6(flags & dht_network::bind_ipv6)
{
error_code ec;
sock().open(asio::ip::udp::v4());
sock().bind(asio::ip::udp::endpoint(lt::address_v4::any(), 6881));
sock().open(m_ipv6 ? asio::ip::udp::v6() : asio::ip::udp::v4());
sock().bind(asio::ip::udp::endpoint(
m_ipv6 ? lt::address(lt::address_v6::any()) : lt::address(lt::address_v4::any()), 6881));
udp::socket::non_blocking_io ioc(true);
sock().io_control(ioc);
@ -116,7 +122,7 @@ struct dht_node final : lt::dht::udp_socket_interface
// reserving space in the vector before emplacing any nodes).
dht_node(dht_node&& n) noexcept
: m_socket(std::move(n.m_socket))
, m_dht(ipv4, this, n.m_dht.settings(), n.m_dht.nid()
, m_dht(n.m_ipv6 ? ipv6 : ipv4, this, n.m_dht.settings(), n.m_dht.nid()
, n.m_dht.observer(), n.m_dht.stats_counters()
, std::map<std::string, lt::dht::node*>())
{
@ -217,7 +223,8 @@ struct dht_node final : lt::dht::udp_socket_interface
dht::node_id const mask = dht::generate_prefix_mask(bucket + 1);
dht::node_id target = dht::generate_random_id() & ~mask;
target |= id & mask;
dht().m_table.node_seen(target, rand_udp_ep(), (lt::random() % 300) + 10);
dht().m_table.node_seen(target, rand_udp_ep(m_ipv6 ? rand_v6 : rand_v4)
, (lt::random() % 300) + 10);
}
}
/*
@ -256,10 +263,11 @@ private:
#endif
lt::udp::endpoint m_ep;
bool m_add_dead_nodes;
bool m_ipv6;
char m_buffer[1300];
};
dht_network::dht_network(sim::simulation& sim, int num_nodes)
dht_network::dht_network(sim::simulation& sim, int num_nodes, std::uint32_t flags)
{
m_sett.ignore_dark_internet = false;
m_sett.restrict_routing_ips = false;
@ -273,7 +281,7 @@ dht_network::dht_network(sim::simulation& sim, int num_nodes)
for (int i = 0; i < num_nodes; ++i)
{
// node 0 is the one we log
m_nodes.emplace_back(sim, m_sett, m_cnt, i, 0/*, dht_node::add_dead_nodes*/);
m_nodes.emplace_back(sim, m_sett, m_cnt, i, flags);
all_nodes.push_back(m_nodes.back().node_info());
}

View File

@ -56,7 +56,13 @@ void print_routing_table(std::vector<lt::dht_routing_bucket> const& rt);
struct dht_network
{
dht_network(sim::simulation& sim, int num_nodes);
enum flags_t
{
add_dead_nodes = 1,
bind_ipv6 = 2
};
dht_network(sim::simulation& sim, int num_nodes, std::uint32_t flags = 0);
~dht_network();
void stop();

View File

@ -51,6 +51,49 @@ POSSIBILITY OF SUCH DAMAGE.
namespace lt = libtorrent;
#ifndef TORRENT_DISABLE_DHT
void bootstrap_session(std::vector<dht_network*> networks, lt::session& ses)
{
lt::dht_settings sett;
sett.ignore_dark_internet = false;
ses.set_dht_settings(sett);
lt::entry state;
for (auto dht : networks)
{
// bootstrap off of 8 of the nodes
auto router_nodes = dht->router_nodes();
char const* nodes_key;
if (router_nodes.front().address().is_v6())
nodes_key = "nodes6";
else
nodes_key = "nodes";
lt::entry::list_type& nodes = state["dht state"][nodes_key].list();
for (auto const& n : router_nodes)
{
std::string node;
std::back_insert_iterator<std::string> out(node);
lt::detail::write_endpoint(n, out);
nodes.push_back(lt::entry(node));
}
}
std::vector<char> buf;
lt::bencode(std::back_inserter(buf), state);
lt::bdecode_node e;
lt::error_code ec;
lt::bdecode(&buf[0], &buf[0] + buf.size(), e, ec);
ses.load_state(e);
lt::settings_pack pack;
pack.set_bool(lt::settings_pack::enable_dht, true);
ses.apply_settings(pack);
}
#endif // TORRENT_DISABLE_DHT
TORRENT_TEST(dht_bootstrap)
{
#ifndef TORRENT_DISABLE_DHT
@ -89,31 +132,7 @@ TORRENT_TEST(dht_bootstrap)
{
if (ticks == 0)
{
lt::dht_settings sett;
sett.ignore_dark_internet = false;
ses.set_dht_settings(sett);
// bootstrap off of 8 of the nodes
lt::entry state;
lt::entry::list_type& nodes = state["dht state"]["nodes"].list();
for (auto const& n : dht.router_nodes())
{
std::string node;
std::back_insert_iterator<std::string> out(node);
lt::detail::write_endpoint(n, out);
nodes.push_back(lt::entry(node));
}
std::vector<char> buf;
lt::bencode(std::back_inserter(buf), state);
lt::bdecode_node e;
lt::error_code ec;
lt::bdecode(&buf[0], &buf[0] + buf.size(), e, ec);
ses.load_state(e);
lt::settings_pack pack;
pack.set_bool(lt::settings_pack::enable_dht, true);
ses.apply_settings(pack);
bootstrap_session({&dht}, ses);
}
if (ticks > 2)
{
@ -133,3 +152,196 @@ TORRENT_TEST(dht_bootstrap)
}
TORRENT_TEST(dht_dual_stack_get_peers)
{
#ifndef TORRENT_DISABLE_DHT
sim::default_config cfg;
sim::simulation sim{ cfg };
dht_network dht(sim, 100);
dht_network dht6(sim, 100, dht_network::bind_ipv6);
lt::sha1_hash const test_ih("01234567890123456789");
bool got_peer_v4 = false, got_peer_v6 = false;
setup_swarm(1, swarm_test::download, sim
// add session
, [](lt::settings_pack& pack) {
}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [&](lt::alert const* a, lt::session& ses)
{
if (lt::dht_get_peers_reply_alert const* p = lt::alert_cast<lt::dht_get_peers_reply_alert>(a))
{
std::vector<lt::tcp::endpoint> peers;
p->peers(peers);
for (lt::tcp::endpoint const& peer : peers)
{
// TODO: verify that the endpoint matches the session's
got_peer_v4 |= peer.address().is_v4();
got_peer_v6 |= peer.address().is_v6();
}
}
}
// terminate?
, [&](int ticks, lt::session& ses) -> bool
{
if (ticks == 0)
{
bootstrap_session({&dht, &dht6}, ses);
}
if (ticks == 2)
{
ses.dht_announce(test_ih, 6881);
}
if (ticks == 4)
{
ses.dht_get_peers(test_ih);
}
if (ticks == 6)
{
TEST_CHECK(got_peer_v4);
TEST_CHECK(got_peer_v6);
return true;
}
return false;
});
sim.run();
#endif // TORRENT_DISABLE_DHT
}
TORRENT_TEST(dht_dual_stack_immutable_item)
{
#ifndef TORRENT_DISABLE_DHT
sim::default_config cfg;
sim::simulation sim{ cfg };
dht_network dht(sim, 100);
dht_network dht6(sim, 100, dht_network::bind_ipv6);
lt::sha1_hash item_hash;
bool got_item = false;
setup_swarm(1, swarm_test::download, sim
// add session
, [](lt::settings_pack& pack) {
}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [&](lt::alert const* a, lt::session& ses)
{
if (lt::dht_immutable_item_alert const* p = lt::alert_cast<lt::dht_immutable_item_alert>(a))
{
// we should only get one alert for each request
TEST_CHECK(!got_item);
got_item = p->target == item_hash && p->item.string() == "immutable item";
}
}
// terminate?
, [&](int ticks, lt::session& ses) -> bool
{
if (ticks == 0)
{
bootstrap_session({&dht, &dht6}, ses);
}
if (ticks == 2)
{
item_hash = ses.dht_put_item(lt::entry("immutable item"));
}
if (ticks == 4)
{
ses.dht_get_item(item_hash);
}
if (ticks == 6)
{
TEST_CHECK(got_item);
return true;
}
return false;
});
sim.run();
#endif // TORRENT_DISABLE_DHT
}
TORRENT_TEST(dht_dual_stack_mutable_item)
{
#ifndef TORRENT_DISABLE_DHT
sim::default_config cfg;
sim::simulation sim{ cfg };
dht_network dht(sim, 100);
dht_network dht6(sim, 100, dht_network::bind_ipv6);
boost::array<char, ed25519_private_key_size> sk;
boost::array<char, ed25519_public_key_size> pk;
int put_count = 0;
bool got_item = false;
setup_swarm(1, swarm_test::download, sim
// add session
, [](lt::settings_pack& pack) {
}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [&](lt::alert const* a, lt::session& ses)
{
if (lt::dht_mutable_item_alert const* p = lt::alert_cast<lt::dht_mutable_item_alert>(a))
{
TEST_CHECK(!got_item);
if (p->authoritative)
got_item = p->key == pk && p->item.string() == "mutable item";
}
}
// terminate?
, [&](int ticks, lt::session& ses) -> bool
{
if (ticks == 0)
{
bootstrap_session({&dht, &dht6}, ses);
}
if (ticks == 2)
{
boost::array<unsigned char, ed25519_seed_size> seed;
ed25519_create_keypair((unsigned char*)pk.data()
, (unsigned char*)sk.data(), seed.data());
ses.dht_put_item(pk, [&](lt::entry& item, boost::array<char, 64>& sig
, boost::uint64_t& seq, std::string const& salt)
{
item = "mutable item";
seq = 1;
std::vector<char> v;
lt::bencode(std::back_inserter(v), item);
lt::dht::sign_mutable_item(
std::make_pair(v.data(), v.size()), std::make_pair(salt.data(), salt.size())
, seq, pk.data(), sk.data(), sig.data());
put_count++;
});
}
if (ticks == 4)
{
// should be one for each stack, ipv4 and ipv6
TEST_EQUAL(put_count, 2);
ses.dht_get_item(pk);
}
if (ticks == 6)
{
TEST_CHECK(got_item);
return true;
}
return false;
});
sim.run();
#endif // TORRENT_DISABLE_DHT
}

View File

@ -124,10 +124,10 @@ tcp::endpoint rand_tcp_ep()
return tcp::endpoint(rand_v4(), g_port + 1024);
}
udp::endpoint rand_udp_ep()
udp::endpoint rand_udp_ep(libtorrent::address(&rand_addr)())
{
g_port = (g_port + 1) % 14037;
return udp::endpoint(rand_v4(), g_port + 1024);
return udp::endpoint(rand_addr(), g_port + 1024);
}
std::map<std::string, boost::int64_t> get_counters(libtorrent::session& s)

View File

@ -57,7 +57,7 @@ EXPORT libtorrent::address rand_v4();
EXPORT libtorrent::address rand_v6();
#endif
EXPORT libtorrent::tcp::endpoint rand_tcp_ep();
EXPORT libtorrent::udp::endpoint rand_udp_ep();
EXPORT libtorrent::udp::endpoint rand_udp_ep(libtorrent::address(&rand_addr)() = rand_v4);
EXPORT libtorrent::sha1_hash rand_hash();