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

fix BEP-40 peer priority for IPv6 #7743

Merged
merged 2 commits into from
Oct 2, 2024
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
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
39 changes: 25 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,30 @@ 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)
{
#if defined __GNUC__ && __GNUC__ >= 7
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
b1[i] &= 0x55;
b2[i] &= 0x55;
#if defined __GNUC__ && __GNUC__ >= 7
#pragma GCC diagnostic pop
#endif
}
}
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")
);
}
}
Loading