Skip to content

Commit

Permalink
fix BEP-40 peer priority for IPv6
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn committed Oct 2, 2024
1 parent 26d8f5c commit 93af9ed
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 28 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

2.0.11 not released

* fix BEP-40 peer priority for IPv6
* limit piece size in torrent creator
* fix file pre-allocation when changing file priority (HanabishiRecca)
* fix uTP issue where closing the connection could corrupt the payload
Expand Down
32 changes: 18 additions & 14 deletions src/torrent_peer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,16 @@ namespace libtorrent {
// 4. if IPs are not in the same /16, mask the IPs by 0xffff5555, hash them
// ordered, lowest first.
//
// * for IPv6 peers, just use the first 64 bits and widen the masks.
// like this: 0xffff5555 -> 0xffffffff55555555
// the lower 64 bits are always unmasked
// * for IPv6 addresses, the lower 48 bits are always unmasked
//
// * for IPv6 addresses, compare /32 and /48 instead of /16 and /24
// * for IPv6 addresses, compare /48, /56, /64, /72 and so on.
//
// * the two IP addresses that are used to calculate the rank must
// always be of the same address family
//
// * all IP addresses are in network byte order when hashed
// The full specification is here:
// https://www.bittorrent.org/beps/bep_0040.html
std::uint32_t peer_priority(tcp::endpoint e1, tcp::endpoint e2)
{
TORRENT_ASSERT(aux::is_v4(e1) == aux::is_v4(e2));
Expand All @@ -95,19 +95,23 @@ namespace libtorrent {
}
else if (aux::is_v6(e1))
{
static const std::uint8_t v6mask[][8] = {
{ 0xff, 0xff, 0xff, 0xff, 0x55, 0x55, 0x55, 0x55 },
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55, 0x55 },
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
};

if (e1 > e2) swap(e1, e2);
address_v6::bytes_type b1 = e1.address().to_v6().to_bytes();
address_v6::bytes_type b2 = e2.address().to_v6().to_bytes();
int const mask = std::memcmp(b1.data(), b2.data(), 4) ? 0
: std::memcmp(b1.data(), b2.data(), 6) ? 1 : 2;
apply_mask(b1.data(), v6mask[mask], 8);
apply_mask(b2.data(), v6mask[mask], 8);
size_t offset = 0xff;
for (size_t i = 0; i < b1.size(); ++i)
{
// we never mask the first 6 bytes, index 6 (the 7th byte)
// is the earliest we start masking at. But if the prefix is
// identical, we keep pushing out where we start masking
if (offset == 0xff && b1[i] != b2[i])
offset = std::max(i + 1, size_t(5));
else if (i > offset)
{
b1[i] &= 0x55;
b2[i] &= 0x55;
}
}
std::uint64_t addrbuf[4];
memcpy(&addrbuf[0], b1.data(), 16);
memcpy(&addrbuf[2], b2.data(), 16);
Expand Down
20 changes: 20 additions & 0 deletions test/test_crc32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,25 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/byteswap.hpp"
#include "test.hpp"

#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <boost/crc.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp"

namespace {
std::uint32_t crc32c_buffer(char const* buf, int const len)
{
boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
crc.process_block(buf, buf + len);
return crc.checksum();
}
}

TORRENT_TEST(crc32)
{
using namespace lt;

std::uint32_t out;
std::uint32_t out2;

std::uint32_t in1 = aux::host_to_network(0xeffea55a);
out = crc32c_32(in1);
Expand All @@ -54,16 +68,22 @@ TORRENT_TEST(crc32)
// https://tools.ietf.org/html/rfc3720#appendix-B.4
out = crc32c(buf, 4);
TEST_EQUAL(out, 0x8a9136aaU);
out2 = crc32c_buffer(reinterpret_cast<char const*>(buf), 32);
TEST_EQUAL(out, out2);

memcpy(buf, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 32);
out = crc32c(buf, 4);
TEST_EQUAL(out, 0x62a8ab43U);
out2 = crc32c_buffer(reinterpret_cast<char const*>(buf), 32);
TEST_EQUAL(out, out2);

memcpy(buf, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", 32);
out = crc32c(buf, 4);
TEST_EQUAL(out, 0x46dd794eU);
out2 = crc32c_buffer(reinterpret_cast<char const*>(buf), 32);
TEST_EQUAL(out, out2);

#if !TORRENT_HAS_ARM
TORRENT_ASSERT(!aux::arm_crc32c_support);
Expand Down
69 changes: 55 additions & 14 deletions test/test_peer_priority.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ POSSIBILITY OF SUCH DAMAGE.
using namespace lt;

namespace {
std::uint32_t hash_buffer(char const* buf, int len)
std::uint32_t hash_buffer(std::string const& hex)
{
std::vector<char> buffer(hex.size() / 2);
aux::from_hex(hex, buffer.data());
boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
crc.process_block(buf, buf + len);
crc.process_block(buffer.data(), buffer.data() + buffer.size());
return crc.checksum();
}
} // anonymous namespace
Expand All @@ -56,21 +58,21 @@ TORRENT_TEST(peer_priority)
// when the IP is the same, we hash the ports, sorted
std::uint32_t p = peer_priority(
ep("230.12.123.3", 0x4d2), ep("230.12.123.3", 0x12c));
TEST_EQUAL(p, hash_buffer("\x01\x2c\x04\xd2", 4));
TEST_EQUAL(p, hash_buffer("012c04d2"));

// when we're in the same /24, we just hash the IPs
p = peer_priority(ep("230.12.123.1", 0x4d2), ep("230.12.123.3", 0x12c));
TEST_EQUAL(p, hash_buffer("\xe6\x0c\x7b\x01\xe6\x0c\x7b\x03", 8));
TEST_EQUAL(p, hash_buffer("e60c7b01e60c7b03"));

// when we're in the same /16, we just hash the IPs masked by
// 0xffffff55
p = peer_priority(ep("230.12.23.1", 0x4d2), ep("230.12.123.3", 0x12c));
TEST_EQUAL(p, hash_buffer("\xe6\x0c\x17\x01\xe6\x0c\x7b\x01", 8));
TEST_EQUAL(p, hash_buffer("e60c1701e60c7b01"));

// when we're in different /16, we just hash the IPs masked by
// 0xffff5555
p = peer_priority(ep("230.120.23.1", 0x4d2), ep("230.12.123.3", 0x12c));
TEST_EQUAL(p, hash_buffer("\xe6\x0c\x51\x01\xe6\x78\x15\x01", 8));
TEST_EQUAL(p, hash_buffer("e60c5101e6781501"));

// test vectors from BEP 40
TEST_EQUAL(peer_priority(ep("123.213.32.10", 0), ep("98.76.54.32", 0))
Expand All @@ -82,18 +84,57 @@ TORRENT_TEST(peer_priority)

if (supports_ipv6())
{
// IPv6 has a twice as wide mask, and we only care about the top 64 bits
// when the IPs are the same, just hash the ports
// if the IPs are identical, order and hash the ports
p = peer_priority(
ep("ffff:ffff:ffff:ffff::1", 0x4d2), ep("ffff:ffff:ffff:ffff::1", 0x12c));
TEST_EQUAL(p, hash_buffer("\x01\x2c\x04\xd2", 4));
ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x4d2)
, ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x12c)
);
TEST_EQUAL(p, hash_buffer("012c04d2"));
// the order doesn't matter
p = peer_priority(
ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x12c)
, ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x4d2)
);
TEST_EQUAL(p, hash_buffer("012c04d2"));

// these IPs don't belong to the same /32, so apply the full mask
// 0xffffffff55555555
// 0xffffffffffff55555555555555555555
p = peer_priority(
ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x4d2)
, ep("ffff:0fff:ffff:ffff:ffff:ffff:ffff:ffff", 0x12c)
);
TEST_EQUAL(p, hash_buffer(
"ffff0fffffff55555555555555555555"
"ffffffffffff55555555555555555555")
);

p = peer_priority(
ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x4d2)
, ep("ffff:ffff:0fff:ffff:ffff:ffff:ffff:ffff", 0x12c)
);
TEST_EQUAL(p, hash_buffer(
"ffffffff0fff55555555555555555555"
"ffffffffffff55555555555555555555")
);

// these share the same /48
p = peer_priority(
ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x4d2)
, ep("ffff:ffff:ff0f:ffff:ffff:ffff:ffff:ffff", 0x12c)
);
TEST_EQUAL(p, hash_buffer(
"ffffffffff0fff555555555555555555"
"ffffffffffffff555555555555555555")
);

// these share the same /56
p = peer_priority(
ep("ffff:ffff:ffff:ffff::1", 0x4d2), ep("ffff:0fff:ffff:ffff::1", 0x12c));
ep("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0x4d2)
, ep("ffff:ffff:ffff:0fff:ffff:ffff:ffff:ffff", 0x12c)
);
TEST_EQUAL(p, hash_buffer(
"\xff\xff\x0f\xff\x55\x55\x55\x55\x00\x00\x00\x00\x00\x00\x00\x01"
"\xff\xff\xff\xff\x55\x55\x55\x55\x00\x00\x00\x00\x00\x00\x00\x01", 32));
"ffffffffffff0fff5555555555555555"
"ffffffffffffffff5555555555555555")
);
}
}

0 comments on commit 93af9ed

Please sign in to comment.