Skip to content

Commit

Permalink
ipv6: use absl:uint128 instead of array of uint_8 for address return. (
Browse files Browse the repository at this point in the history
…envoyproxy#2431)

Signed-off-by: Constance Caramanolis <[email protected]>
  • Loading branch information
ccaraman authored and mattklein123 committed Jan 26, 2018
1 parent d6b630a commit 4264986
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 51 deletions.
4 changes: 4 additions & 0 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ def _com_google_absl():
name = "abseil_strings",
actual = "@com_google_absl//absl/strings:strings",
)
native.bind(
name = "abseil_int128",
actual = "@com_google_absl//absl/numeric:int128",
)

def _com_google_protobuf():
_repository_impl("com_google_protobuf")
Expand Down
2 changes: 1 addition & 1 deletion bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
REPOSITORY_LOCATIONS = dict(
com_google_absl = dict(
commit = "6de53819a7173bd446156237a37f53464b7732cc",
commit = "787891a3882795cee0364e8a0f0dda315578d155",
remote = "https://github.com/abseil/abseil-cpp",
),
com_github_bombela_backward = dict(
Expand Down
1 change: 1 addition & 0 deletions include/envoy/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ envoy_cc_library(
envoy_cc_library(
name = "connection_interface",
hdrs = ["connection.h"],
external_deps = ["abseil_int128"],
deps = [
":address_interface",
":filter_interface",
Expand Down
6 changes: 4 additions & 2 deletions include/envoy/network/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include "envoy/common/pure.h"

#include "absl/numeric/int128.h"

namespace Envoy {
namespace Network {
namespace Address {
Expand All @@ -35,9 +37,9 @@ class Ipv6 {
virtual ~Ipv6() {}

/**
* @return the 16-byte IPv6 address in network byte order.
* @return the absl::uint128 IPv6 address in network byte order.
*/
virtual std::array<uint8_t, 16> address() const PURE;
virtual absl::uint128 address() const PURE;
};

enum class IpVersion { v4, v6 };
Expand Down
8 changes: 4 additions & 4 deletions source/common/network/address_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,10 @@ int Ipv4Instance::connect(int fd) const {

int Ipv4Instance::socket(SocketType type) const { return socketFromSocketType(type); }

std::array<uint8_t, 16> Ipv6Instance::Ipv6Helper::address() const {
std::array<uint8_t, 16> result;
std::copy(std::begin(address_.sin6_addr.s6_addr), std::end(address_.sin6_addr.s6_addr),
std::begin(result));
absl::uint128 Ipv6Instance::Ipv6Helper::address() const {
absl::uint128 result{0};
static_assert(sizeof(absl::uint128) == 16, "The size of asbl::uint128 is not 16.");
memcpy(&result, &address_.sin6_addr.s6_addr, sizeof(absl::uint128));
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion source/common/network/address_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class Ipv6Instance : public InstanceBase {

private:
struct Ipv6Helper : public Ipv6 {
std::array<uint8_t, 16> address() const override;
absl::uint128 address() const override;
uint32_t port() const;

std::string makeFriendlyAddress() const;
Expand Down
51 changes: 14 additions & 37 deletions source/common/network/cidr_range.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,9 @@ bool CidrRange::isInRange(const Instance& address) const {
}
break;
case IpVersion::v6:
int length = length_;
// Loop through address bytes and compare. Address is in network byte order.
for (int i = 0; i < 16; i++) {
if (length < 8) {
// Compare relevant bits.
return (address.ip()->ipv6()->address()[i] >> (8 - length) ==
address_->ip()->ipv6()->address()[i] >> (8 - length));
} else {
if (address.ip()->ipv6()->address()[i] == address_->ip()->ipv6()->address()[i]) {
if (length == 8) {
return true;
} else {
length -= 8;
}
} else {
break;
}
}
if ((Utility::Ip6ntohl(address_->ip()->ipv6()->address()) >> (128 - length_)) ==
(Utility::Ip6ntohl(address.ip()->ipv6()->address()) >> (128 - length_))) {
return true;
}
break;
}
Expand Down Expand Up @@ -193,28 +178,20 @@ InstanceConstSharedPtr CidrRange::truncateIpAddressAndLength(InstanceConstShared
// Create an Ipv6Instance with only a port, which will thus have the any address.
return std::make_shared<Ipv6Instance>(uint32_t(0));
}
// We need to mask out the unused bits, but we don't have a uint128_t available.
// However, we know that the array returned by Ipv6::address() represents the address
// in network order (bit 7 of byte 0 is the high-order bit of the address), so we
// simply need to keep the leading length bits of the array, and get rid of the rest.
sockaddr_in6 sa6;
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(0);
std::array<uint8_t, 16> ip6 = address->ip()->ipv6()->address();
for (int i = 0; i < 16; ++i) {
if (length == 0) {
// We've retained all the bits we need, the remaining are all zero.
sa6.sin6_addr.s6_addr[i] = 0;
} else if (length < 8) {
// We're retaining only the high-order length bits of this byte.
sa6.sin6_addr.s6_addr[i] = ip6[i] & (0xff << (8 - length));
length = 0;
} else {
// We're keeping all of these bits.
sa6.sin6_addr.s6_addr[i] = ip6[i];
length -= 8;
}
}

// The maximum number stored in absl::uint128 has every bit set to 1.
absl::uint128 mask = absl::Uint128Max();
// Shifting the value to the left sets all bits between 128-length and 128 to zero.
mask <<= (128 - length);
// This will mask out the unused bits of the address.
absl::uint128 ip6 = Utility::Ip6ntohl(address->ip()->ipv6()->address()) & mask;

absl::uint128 ip6_htonl = Utility::Ip6htonl(ip6);
static_assert(sizeof(absl::uint128) == 16, "The size of asbl::uint128 is not 16.");
memcpy(&sa6.sin6_addr.s6_addr, &ip6_htonl, sizeof(absl::uint128));
return std::make_shared<Ipv6Instance>(sa6);
}
}
Expand Down
37 changes: 33 additions & 4 deletions source/common/network/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,12 @@ bool Utility::isInternalAddress(const Address::Instance& address) {
// Local IPv6 address prefix defined in RFC4193. Local addresses have prefix FC00::/7.
// Currently, the FD00::/8 prefix is locally assigned and FC00::/8 may be defined in the
// future.
const auto address6 = address.ip()->ipv6()->address();
const uint8_t* address6_bytes = reinterpret_cast<const uint8_t*>(address6.data());
static_assert(sizeof(absl::uint128) == sizeof(in6addr_loopback),
"sizeof(absl::uint128) != sizeof(in6addr_loopback)");
const absl::uint128 address6 = address.ip()->ipv6()->address();
const uint8_t* address6_bytes = reinterpret_cast<const uint8_t*>(&address6);
if (address6_bytes[0] == 0xfd ||
memcmp(address6_bytes, &in6addr_loopback, sizeof(in6addr_loopback)) == 0) {
memcmp(&address6, &in6addr_loopback, sizeof(in6addr_loopback)) == 0) {
return true;
}

Expand All @@ -240,7 +242,9 @@ bool Utility::isLoopbackAddress(const Address::Instance& address) {
// Compare to the canonical v4 loopback address: 127.0.0.1.
return address.ip()->ipv4()->address() == htonl(INADDR_LOOPBACK);
} else if (address.ip()->version() == Address::IpVersion::v6) {
std::array<uint8_t, 16> addr = address.ip()->ipv6()->address();
static_assert(sizeof(absl::uint128) == sizeof(in6addr_loopback),
"sizeof(absl::uint128) != sizeof(in6addr_loopback)");
absl::uint128 addr = address.ip()->ipv6()->address();
return 0 == memcmp(&addr, &in6addr_loopback, sizeof(in6addr_loopback));
}
NOT_IMPLEMENTED;
Expand Down Expand Up @@ -342,5 +346,30 @@ bool Utility::portInRangeList(const Address::Instance& address, const std::list<
return false;
}

absl::uint128 Utility::Ip6ntohl(const absl::uint128& address) {
// TODO(ccaraman): Support Ip6ntohl for big-endian.
static_assert(ABSL_IS_LITTLE_ENDIAN,
"Machines using big-endian byte order is not supported for IPv6.");
return flipOrder(address);
}

absl::uint128 Utility::Ip6htonl(const absl::uint128& address) {
// TODO(ccaraman): Support Ip6ntohl for big-endian.
static_assert(ABSL_IS_LITTLE_ENDIAN,
"Machines using big-endian byte order is not supported for IPv6.");
return flipOrder(address);
}

absl::uint128 Utility::flipOrder(const absl::uint128& input) {
absl::uint128 result{0};
absl::uint128 data = input;
for (int i = 0; i < 16; i++) {
result <<= 8;
result |= (data & 0x000000000000000000000000000000FF);
data >>= 8;
}
return result;
}

} // namespace Network
} // namespace Envoy
23 changes: 23 additions & 0 deletions source/common/network/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,31 @@ class Utility {
*/
static bool portInRangeList(const Address::Instance& address, const std::list<PortRange>& list);

/**
* Converts IPv6 absl::uint128 in network byte order to host byte order.
* @param address supplies the IPv6 address in network byte order.
* @return the absl::uint128 IPv6 address in host byte order.
*/
static absl::uint128 Ip6ntohl(const absl::uint128& address);

/**
* Converts IPv6 absl::uint128 in host byte order to network byte order.
* @param address supplies the IPv6 address in host byte order.
* @return the absl::uint128 IPv6 address in network byte order.
*/
static absl::uint128 Ip6htonl(const absl::uint128& address);

private:
static void throwWithMalformedIp(const std::string& ip_address);

/**
* Takes a number and flips the order in byte chunks. The last byte of the input will be the
* first byte in the output. The second to last byte will be the second to first byte in the
* output. Etc..
* @param input supplies the input to have the bytes flipped.
* @return the absl::uint128 of the input having the bytes flipped.
*/
static absl::uint128 flipOrder(const absl::uint128& input);
};

} // namespace Network
Expand Down
4 changes: 2 additions & 2 deletions source/common/ssl/ssl_socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) {
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_WANT_WRITE:
// Renegotiation has started. We don't handle renegotiation so just fall through.
// Renegotiation has started. We don't handle renegotiation so just fall through.
default:
drainErrorQueue();
action = PostIoAction::Close;
Expand Down Expand Up @@ -178,7 +178,7 @@ Network::IoResult SslSocket::doWrite(Buffer::Instance& write_buffer) {
keep_writing = false;
break;
case SSL_ERROR_WANT_READ:
// Renegotiation has started. We don't handle renegotiation so just fall through.
// Renegotiation has started. We don't handle renegotiation so just fall through.
default:
drainErrorQueue();
return {PostIoAction::Close, total_bytes_written};
Expand Down
41 changes: 41 additions & 0 deletions test/common/network/utility_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -273,5 +273,46 @@ TEST(PortRangeListTest, Normal) {
}
}

// TODO(ccaraman): Support big-endian. These tests operate under the assumption that the machine
// byte order is little-endian.
TEST(AbslUint128, TestByteOrder) {
{
Address::Ipv6Instance address("::1");
uint64_t high = 0x100000000000000;
EXPECT_EQ(absl::MakeUint128(high, 0), address.ip()->ipv6()->address());
EXPECT_EQ(absl::MakeUint128(high, 0),
Utility::Ip6htonl(Utility::Ip6ntohl(address.ip()->ipv6()->address())));

EXPECT_EQ(absl::uint128(1), Utility::Ip6ntohl(address.ip()->ipv6()->address()));
}
{
Address::Ipv6Instance address("1::");
EXPECT_EQ(absl::uint128(256), address.ip()->ipv6()->address());
EXPECT_EQ(absl::uint128(256),
Utility::Ip6htonl(Utility::Ip6ntohl(address.ip()->ipv6()->address())));

uint64_t high = 0x001000000000000;
EXPECT_EQ(absl::MakeUint128(high, 0), Utility::Ip6ntohl(address.ip()->ipv6()->address()));
}
{
Address::Ipv6Instance address("2001:abcd:ef01:2345:6789:abcd:ef01:234");
uint64_t low = 0x452301EFCDAB0120;
uint64_t high = 0x340201EFCDAB8967;
EXPECT_EQ(absl::MakeUint128(high, low), address.ip()->ipv6()->address());
EXPECT_EQ(absl::MakeUint128(high, low),
Utility::Ip6htonl(Utility::Ip6ntohl(address.ip()->ipv6()->address())));
}
{
Address::Ipv6Instance address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
EXPECT_EQ(absl::Uint128Max(), address.ip()->ipv6()->address());
EXPECT_EQ(absl::Uint128Max(), Utility::Ip6ntohl(address.ip()->ipv6()->address()));
}
{
TestRandomGenerator rand;
absl::uint128 random_number = absl::MakeUint128(rand.random(), rand.random());
EXPECT_EQ(random_number, Utility::Ip6htonl(Utility::Ip6ntohl(random_number)));
}
}

} // namespace Network
} // namespace Envoy

0 comments on commit 4264986

Please sign in to comment.