Skip to content

Conversation

ccaraman
Copy link
Contributor

Description: Change IPv6 to return absl::uint128 instead of array of uint8_t. Equivalent ntohl and htonl have been implemented in Utility.

Risk Level: Low.

Testing: Existing Ipv6 tests and extended utility unit tests to cover new method.

Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, few comments

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};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether the type is intrinsic or not, you should be able to just memcpy here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated all call paths to use memcpy instead.


absl::uint128 Utility::Ip6htonl(const absl::uint128& address) { return flipOrder(address); }

std::array<uint8_t, 16> Utility::getArrayRepresentation(const absl::uint128& address) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method is not needed. memcpy should work where this is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated all call paths to use memcpy.

@@ -342,5 +342,30 @@ bool Utility::portInRangeList(const Address::Instance& address, const std::list<
return false;
}

absl::uint128 Utility::Ip6ntohl(const absl::uint128& address) { return flipOrder(address); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it makes sense that both of these functions do the same thing unless you actually check for local endianness somewhere. I would either do that, or use a single function, assert endianness somehow, and add a TODO.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this break if Envoy is running on a big endian system?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been updated to have an assert on little endianess and a todo has been added to support big endian.

}
absl::uint128 ip6 = Utility::Ip6ntohl(address->ip()->ipv6()->address());
// The maximum number stored in absl::uint128 has every bit set to 1.
absl::uint128 max_int = absl::MakeUint128(std::numeric_limits<uint64_t>::max(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use absl::Uint128Max()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I try using it, I get the following: error: 'Uint128Max' is not a member of 'absl' absl::uint128 max_int = absl::Uint128Max(); ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figured it out. There was an update 11 days ago to change kuint128max to Uint128Max(). I locally have the older absl.

max_int <<= (128 - length);
ip6 &= max_int;

std::array<uint8_t, 16> ip6_array = Utility::getArrayRepresentation(Utility::Ip6htonl(ip6));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be const std::array

@@ -342,5 +342,30 @@ bool Utility::portInRangeList(const Address::Instance& address, const std::list<
return false;
}

absl::uint128 Utility::Ip6ntohl(const absl::uint128& address) { return flipOrder(address); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this break if Envoy is running on a big endian system?

/**
* Converts IPv6 absl::uint128 in network byte order to host byte order.
* @param address supplies the IPv6 address in network byte order.
* @return IPv6 in host byte order.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: return type in @return (here and the other functions in this file)

EXPECT_EQ(expected_result, Utility::getArrayRepresentation(address.ip()->ipv6()->address()));
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth thinking about how this test will work (or won't work) on a big endian machine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added an assert to only work on big endian machines.

Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
}

bool Utility::isLittleEndian() {
int num = 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absl already has a define for this which I think we implicitly include. Can we just use that instead of reimplementing the logic?

std::begin(result));
absl::uint128 Ipv6Instance::Ipv6Helper::address() const {
absl::uint128 result{0};
memcpy(&result, &address_.sin6_addr.s6_addr, 16);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a static_assert everywhere you do memcpy to make sure sizeof(absl::uint128) is what you expect, and then potentially use sizeof(absl::uint128) instead of 16.

Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
@@ -342,5 +346,28 @@ 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.
ASSERT(ABSL_IS_LITTLE_ENDIAN);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make both this assert and the other similar ones static_assert. We should just prevent the code from compiling on big endian systems until we implement this. With static_assert, the compilation message will be very clear also, much like #error

Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for doing this as its own PR with preexisting tests - it's much easier to trust the code when I don't feel responsible for catching bitmath bugs by eye :-)

std::numeric_limits<uint64_t>::max());
EXPECT_EQ(max_int, address.ip()->ipv6()->address());
EXPECT_EQ(max_int, Utility::Ip6ntohl(address.ip()->ipv6()->address()));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional but I'm a big fan of RNG code for tests like this. What would you think of setting low and high bytes based on TestRandomGenerator and making sure if you ntohl and htonl you end up with the original?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that sounds good. I'll update the tests.

@@ -200,21 +185,17 @@ InstanceConstSharedPtr CidrRange::truncateIpAddressAndLength(InstanceConstShared
sockaddr_in6 sa6;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update the comment "but we don't have a uint128_t available."?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll remove that.

absl::uint128 max_int = absl::Uint128Max();
// Shifting the value to the left set all bits between 128-length and 128 to zero.
max_int <<= (128 - length);
ip6 &= max_int;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/max_int/mask/?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is up to Matt and you but I wouldn't even TODO big-endian support unless we have a big-endian machine to test on just because I'd be concerned we'd screw something up somewhere else without being able to test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking we would be testing this on Google import but looks like that's specifically the Little Endian flavor of PPC (ppc64le). Some kind of compile time assert is probably sufficient to avoid surprises if someone manages to get Envoy build on a BE machine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No real preference on keeping the TODO or removing it. Will leave it up to you all. I basically figured that with the static_assert if someone cares they can come along and remove the static assert, add appropriate code, and test it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave the TODO. I think it helps future contributors know that this is something that Envoy wants to support at a future date.

for (int i = 0; i < 16; i++) {
result <<= 8;
result |= (data & 0x000000000000000000000000000000FF);
data >>= 8;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional : internally I think we roughly return uint128(htonl(input.low), htonl(input.high)) which obviously only works if htonl is flipping bits. If this is only going to be called for little endian switching might save some operations but I'm also fine leaving as is since if we add big endian support we'd need rename it something awful like flipOrderForLittleEndian to make it clear :-P

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave it as is.

Signed-off-by: Constance Caramanolis <ccaramanolis@lyft.com>
@mattklein123 mattklein123 merged commit 4264986 into master Jan 26, 2018
@mattklein123 mattklein123 deleted the absl_uint128 branch January 26, 2018 16:21
mattklein123 added a commit that referenced this pull request Jan 26, 2018
iii-i added a commit to iii-i/envoy that referenced this pull request Mar 4, 2020
This patch resolves the TODO from
envoyproxy#2431.
Tests pass on a big-endian machine (IBM Z).

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
mattklein123 pushed a commit that referenced this pull request Mar 4, 2020
This patch resolves the TODO from
#2431.
Tests pass on a big-endian machine (IBM Z).

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Shikugawa pushed a commit to Shikugawa/envoy that referenced this pull request Mar 28, 2020
Signed-off-by: Douglas Reid <douglas-reid@users.noreply.github.com>
jwendell pushed a commit to maistra/envoy that referenced this pull request May 18, 2020
This patch resolves the TODO from
envoyproxy/envoy#2431.
Tests pass on a big-endian machine (IBM Z).

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants