Skip to content

utility: Fix address comparisons in isLocalConnection(), add tests.#5373

Closed
jrajahalme wants to merge 6 commits intoenvoyproxy:masterfrom
jrajahalme:local-connection-utility
Closed

utility: Fix address comparisons in isLocalConnection(), add tests.#5373
jrajahalme wants to merge 6 commits intoenvoyproxy:masterfrom
jrajahalme:local-connection-utility

Conversation

@jrajahalme
Copy link
Copy Markdown
Contributor

Add test case for LocalConnection test where local and remote
addresses have the same IP address. Fix the test code to actually use
the intended local address. Fix the utility implementation to compare
the actual addresses rather than the pointers to them.

Address comparisons in Utility::isLocalConnection() should be performed on
the IP addresses, ignoring any port numbers. Skip the temporary
Instance in the comparison loop. Add test cases with port numbers.

Fixes: #4682
Signed-off-by: Jarno Rajahalme jarno@covalent.io

Add test case for LocalConnection test where local and remote
addresses have the same IP address. Fix the test code to actually use
the intended local address. Fix the utility implementation to compare
the actual addresses rather than the pointers to them.

Fixes: envoyproxy#4682
Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
Address comparisons in Utility::isLocalConnection() should be
performed on the IP addresses, ignoring any port numbers.  Skip the
temporary Instance in the comparison loop.  Add test cases with port
numbers.

Fixes: envoyproxy#4682
Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
Comment thread source/common/network/utility.cc Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a lot to read in a conditional statement. Is there a way you can add the needed comparison into an equality operator on the appropriate object to make this statement look a lot clearer? Then you can hide the complexity of this and v4 vs. v6 in the right place.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Envoy does not have a generic address type for an IP address without a port number. Typically an IP address type with a zero port number is used to approximate it, but real connection addresses always have non-zero port numbers, so given the current type system this is the only way to implement the comparison here. We could move this to an utility function (e.g., isSameIPAddress(addr1, addr2)) to make it easier to read, though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, that's right. I think it would be best to use a utility function, then.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

@jrajahalme jrajahalme force-pushed the local-connection-utility branch from 3f41d3d to d8337e7 Compare December 20, 2018 21:11
Add a new utility function to compare if two address instances are the
same IP address, ignoring the port numbers.

Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
Comment thread source/common/network/utility.cc Outdated
return false;
}

if (address1.ip()->version() == Address::IpVersion::v4) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This might look less verbose as a switch statement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread source/common/network/utility.cc Outdated

bool Utility::isSameIPAddress(const Address::Instance& address1,
const Address::Instance& address2) {
if (address1.type() != Address::Type::Ip ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe this should be an assert?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread source/common/network/utility.cc Outdated
}
} else {
const auto* addr = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_addr);
absl::uint128 v6addr = remote_address->ip()->ipv6()->address();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: const absl::uint128

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

might be good to call this remote_v6addr for maximum clarity, too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment thread source/common/network/utility.cc Outdated
return true;
}
} else {
const auto* addr = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_addr);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

local_addr?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Used if_addr instead.

Comment thread source/common/network/utility.cc Outdated
if (remote_address == local_address) {
return true;
if (af_look_up == AF_INET) {
const auto* addr = reinterpret_cast<const struct sockaddr_in*>(ifa->ifa_addr);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

local_addr?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ditto.

Restore the order of comparisons to pass tests that count the number
of calls to localAddress().

Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
if (remote_address->type() == Envoy::Network::Address::Type::Pipe ||
remote_address == socket.localAddress() || isLoopbackAddress(*remote_address)) {
if (remote_address->type() != Envoy::Network::Address::Type::Ip ||
isSameIPAddress(*remote_address, *socket.localAddress()) ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry, but could you explain why doesn't this work with ==?

Also, can't we fix the operator== instead of adding an utility function, since presumably this is broken in other places as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

remote_address (as well as socket.localAddress are shared pointers (InstanceConstSharedPtr). Equality operator of pointers compares the pointers themselves, rather than the objects they point to.

Secondly, even if the addresses were compared, this comparison must ignore the port numbers that are embedded in every Envoy Ip address instance.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

operator== is correct for comparing the whole IP address + port combination. It also works in many places where it is known that the port numbers of both addresses are the same (e.g., zero).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You may want to review the two commits separately, as they address the above issues one at a time.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, I completely missed the port part, thanks!

@alyssawilk
Copy link
Copy Markdown
Contributor

Dan: can you assign a senior reviewer when you're done on your pass?


if (remote_address == local_address) {
return true;
if (af_look_up == AF_INET) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you move this comparison into isSameIPAddress(const Address::Instance& address1, const struct sockaddr *address2)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

isSameIPAddress() already checks the IP version, but here, within a loop, we don't want to create temporary IP Address Instances so we are open-coding the comparison.

However, upon a second look I removed the assert in isSameIPAddress() so that it is safe to call on any type of an Address.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note that I'm suggesting extracting this to isSameIPAddress() with different arguments, so you'd invoke it using isSameIPAddress(*remote_address, ifa->ifa_addr) without creating temporary Address::Instance object (which I agree would be an overkill).

Basically, extracting this code for readability, since compiler is going to inline it here anyway.

I'm fine with you leaving it here, if feel strongly about it, though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Friendly ping on what you prefer to do here (leave it as-is, in which case it's ready to be merged, or extracting it to isSameIPAddress(const Address::Instance&, const struct sockaddr *)), to make sure that we're not stuck waiting on each other.

}
} else {
const auto* if_addr = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_addr);
const absl::uint128 remote_v6addr = remote_address->ip()->ipv6()->address();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please make sure to not overread the memory:

static_assert(sizeof(absl::uint128) == sizeof(if_addr->sin6_addr.s6_addr),
              "sizeof(absl::uint128) != sizeof(if_addr->sin6_addr.s6_addr)");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added.


testing::NiceMock<Network::MockConnectionSocket> socket;

EXPECT_CALL(socket, remoteAddress()).WillRepeatedly(testing::ReturnRef(local_addr));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice find!

local_addr.reset(new Network::Address::Ipv6Instance("fabc::42", 80));
remote_addr.reset(new Network::Address::Ipv6Instance(
Utility::getLocalAddress(Address::IpVersion::v6)->ip()->addressAsString(), 23413));
EXPECT_TRUE(Utility::isLocalConnection(socket));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you add a test comparing IPv4 and IPv6 addresses? It looks that we're missing it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added.

if (remote_address->type() == Envoy::Network::Address::Type::Pipe ||
remote_address == socket.localAddress() || isLoopbackAddress(*remote_address)) {
if (remote_address->type() != Envoy::Network::Address::Type::Ip ||
isSameIPAddress(*remote_address, *socket.localAddress()) ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, I completely missed the port part, thanks!

Make isSameIPAddress() safe to call on any type of Address
Instances. Add an assert for IPv6 address size. Add a case with mixed
IPv4/IPv6 addresses to the LocalConnection Test.

Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
@jrajahalme
Copy link
Copy Markdown
Contributor Author

@PiotrSikora Addressed your concerns, can you have another look?

@stale
Copy link
Copy Markdown

stale Bot commented Jan 12, 2019

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale Bot added the stale stalebot believes this issue/PR has not been touched recently label Jan 12, 2019
@stale
Copy link
Copy Markdown

stale Bot commented Jan 19, 2019

This pull request has been automatically closed because it has not had activity in the last 14 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale Bot closed this Jan 19, 2019
@PiotrSikora
Copy link
Copy Markdown
Contributor

Not stale, pending response from @jrajahalme.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale stalebot believes this issue/PR has not been touched recently

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants