From 89b70c5a33e26481b052e97fb11cd649fb619c4f Mon Sep 17 00:00:00 2001 From: arvidn Date: Tue, 1 Oct 2024 10:33:39 +0200 Subject: [PATCH 1/2] fix BEP-40 peer priority for IPv6 --- ChangeLog | 1 + src/torrent_peer.cpp | 32 +++++++++-------- test/test_crc32.cpp | 20 +++++++++++ test/test_peer_priority.cpp | 69 +++++++++++++++++++++++++++++-------- 4 files changed, 94 insertions(+), 28 deletions(-) diff --git a/ChangeLog b/ChangeLog index b31dbba3a1b..9eff5e045ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/src/torrent_peer.cpp b/src/torrent_peer.cpp index 5cf72763025..dd847b2d464 100644 --- a/src/torrent_peer.cpp +++ b/src/torrent_peer.cpp @@ -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)); @@ -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); diff --git a/test/test_crc32.cpp b/test/test_crc32.cpp index a59a10c2eac..f611c1f1e1c 100644 --- a/test/test_crc32.cpp +++ b/test/test_crc32.cpp @@ -37,11 +37,25 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/aux_/byteswap.hpp" #include "test.hpp" +#include "libtorrent/aux_/disable_warnings_push.hpp" +#include +#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); @@ -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(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(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(buf), 32); + TEST_EQUAL(out, out2); #if !TORRENT_HAS_ARM TORRENT_ASSERT(!aux::arm_crc32c_support); diff --git a/test/test_peer_priority.cpp b/test/test_peer_priority.cpp index afb426c6d00..f6042b3ab86 100644 --- a/test/test_peer_priority.cpp +++ b/test/test_peer_priority.cpp @@ -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 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 @@ -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)) @@ -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") + ); } } From cfe3650d04a443be4cefb131145392d870a8db51 Mon Sep 17 00:00:00 2001 From: arvidn Date: Tue, 1 Oct 2024 17:04:08 +0200 Subject: [PATCH 2/2] address GCC false positive warning --- src/torrent_peer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/torrent_peer.cpp b/src/torrent_peer.cpp index dd847b2d464..b84b4b47c53 100644 --- a/src/torrent_peer.cpp +++ b/src/torrent_peer.cpp @@ -108,8 +108,15 @@ namespace libtorrent { 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];