Skip to content

listener: Use v4-mapped addressing consistently.#2572

Merged
htuch merged 6 commits intoenvoyproxy:masterfrom
jrajahalme:fix-v4-compat-remote
Feb 15, 2018
Merged

listener: Use v4-mapped addressing consistently.#2572
htuch merged 6 commits intoenvoyproxy:masterfrom
jrajahalme:fix-v4-compat-remote

Conversation

@jrajahalme
Copy link
Copy Markdown
Contributor

Currently the local address of a new connection is mapped to an IPv4
address instance if the listening socket is not in V6ONLY mode and the
local address of the socket is an IPv4-mapped IPv6 address. This leads
to connections having an (IPv4-mapped) IPv6 instance for the remote
address and an IPv4 instance for the local address, which is
confusing. Fix this by allowing the remote address to be v4-mapped if the
local address is an IPv4 address. Or conversely, set the 'v6only'
parameter to 'true' if the local address is an IPv6 address instance.

Fixes: 3b3c009 ("address_impl: Return an Ipv4Instance for v4-mapped v6 addresses. (#2309)")
Signed-off-by: Jarno Rajahalme jarno@covalent.io
Risk Level: Low

Copy link
Copy Markdown
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.

Can there be some test that would have failed before this change?

Fail if remote and local addresses of a new server connection are of
different address instance types.

Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
Currently the local address of a new connection is mapped to an IPv4
address instance if the listening socket is not in V6ONLY mode and the
local address of the socket is an IPv4-mapped IPv6 address. This leads
to connections having an (IPv4-mapped) IPv6 instance for the remote
address and an IPv4 instance for the local address, which is
confusing. Fix this by allowing the remote address to be v4-mapped if the
local address is an IPv4 address. Or conversely, set the 'v6only'
parameter to 'true' if the local address is an IPv6 address instance.

Fixes: 3b3c009 ("address_impl: Return an Ipv4Instance for v4-mapped v6 addresses. (envoyproxy#2309)")
Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
@jrajahalme
Copy link
Copy Markdown
Contributor Author

Added a test case that fails without the fix.

@htuch
Copy link
Copy Markdown
Member

htuch commented Feb 11, 2018

@zuercher can you take a first pass on this? Thanks.

// Initialized on first call in a thread-safe manner.
static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(static_cast<uint32_t>(0)));
return any;
static sockaddr_in6 v6any = v6any_();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you can just inline this call.

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.

Can you show how would I inline a static initializer for sockaddr_in6, which is different on different OSes and when C++ does not support designated initializers?

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.

Or did you mean adding the inline keyword to v6any_(), making this a call to a static inline function?

*/
Address::InstanceConstSharedPtr getAnyAddress(const Address::IpVersion version);
Address::InstanceConstSharedPtr getAnyAddress(const Address::IpVersion version,
bool v4_compat = false);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Document v4_compat, please.

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.

OK.

TEST_P(ListenerImplTest, WildcardListenerIpv4Compat) {
Stats::IsolatedStoreImpl stats_store;
Event::DispatcherImpl dispatcher;
Network::TcpListenSocket socket(Network::Test::getAnyAddress(version_, true), true);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd like a comment here (or at the top of the test) briefly explaining the circumstances this is testing.

It's confusing to me since this is the only place that leads to invoking Network::Utility::getAnyIpv6Address(false), so is this code testing a case that cannot occur outside of tests?

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.

This test mocks a case where a listener would be configured with an IPv6 any address and have it's socket_address configured with ipv4_compat = true. There are a couple of tests that do that, but in those the socket address comes from the listener configuraion, so they do not invoke Network::Utility::getAnyIpv6Address() at all. The effect is the same, though, when the listener is configured with the IPv6 ANY address ([::]).

* IPv6 address are to be accepted.
*/
static Address::InstanceConstSharedPtr getIpv6AnyAddress();
static Address::InstanceConstSharedPtr getIpv6AnyAddress(bool v6only = true);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is only ever called with v6only = false in a test (that I can see). Perhaps the v6only = false behavior should be handled in Network::Test::getAnyAddress?

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.

I'll do that.

Revert the changes to source/common/network/utility.[h|cc], as those were only needed for testing.

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

@zuercher zuercher left a comment

Choose a reason for hiding this comment

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

Looks good. Just a couple nits and a question. Thanks for working on this.


// Test for the correct behavior when a listener is configured with an ANY address that allows
// receiving IPv4 connections on an IPv6 socket. In this case the address instances of both
// local and remove addresses of the connection should be IPv4 instances, as the connection really
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: remote not remove

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.

Fixed.

// There is no portable way to initialize sockaddr_in6 with a static initializer, do it with a
// helper function instead
static sockaddr_in6& v6any_() {
static sockaddr_in6 v6any = {};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What do you think about dropping the static modifier for this variable? (Since you rely on the function only being called once to initialize the static variable in getAnyAddress.)

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, had to change the function to return the struct to not return a reference to the stack.

}
if (v4_compat) {
static sockaddr_in6 v6any = v6any_();
static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any, false));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would just call v6any_() on this line instead of initializing an extra static var. TIOLI.

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, thanks!

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

@jrajahalme jrajahalme left a comment

Choose a reason for hiding this comment

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

Thanks for the review!

// There is no portable way to initialize sockaddr_in6 with a static initializer, do it with a
// helper function instead
static sockaddr_in6& v6any_() {
static sockaddr_in6 v6any = {};
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, had to change the function to return the struct to not return a reference to the stack.

}
if (v4_compat) {
static sockaddr_in6 v6any = v6any_();
static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any, false));
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, thanks!


// Test for the correct behavior when a listener is configured with an ANY address that allows
// receiving IPv4 connections on an IPv6 socket. In this case the address instances of both
// local and remove addresses of the connection should be IPv4 instances, as the connection really
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.

Fixed.

zuercher
zuercher previously approved these changes Feb 14, 2018
Copy link
Copy Markdown
Member

@zuercher zuercher left a comment

Choose a reason for hiding this comment

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

This looks good to me.

@dnoe can you assign a senior maintainer since Matt is out?

@zuercher
Copy link
Copy Markdown
Member

Sorry, I read the wrong list of names. @htuch I think this is ready for you to look at.

Copy link
Copy Markdown
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

LGTM, just some small cleanup asks.

// Get the local address from the new socket if the listener is listening on IP ANY
// (e.g., 0.0.0.0 for IPv4) (local_address_ is nullptr in this case).
const Address::InstanceConstSharedPtr& local_address =
!listener->local_address_ ? listener->getLocalAddress(fd) : listener->local_address_;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Tiny nit: prefer to negate the conditional here, easier to parse.

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.

Address::InstanceConstSharedPtr getAnyAddress(const Address::IpVersion version) {
// There is no portable way to initialize sockaddr_in6 with a static initializer, do it with a
// helper function instead
static sockaddr_in6 v6any_() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Envoy convention is to put this in the anonymous namespace rather than mark it static. Might be clearer to rename it to sockaddrIn6Any() to match Envoy convention.

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.

Applied.

return Network::Utility::getIpv4AnyAddress();
}
if (v4_compat) {
static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any_(), false));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you add some internal comments here explaining how this (maybe using ASCII address notation) differs from Network::Utility::getIpv6AnyAddress() below?

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 this:

    // This will return an IPv6 ANY address ("[::]:0") like the getIpv6AnyAddress() below, but
    // with the internal 'v6only' member set to false. This will allow a socket created from this
    // address to accept IPv4 connections. IPv4 connections received on IPv6 sockets will have
    // Ipv4-mapped IPv6 addresses, which we will then internally interpret as IPv4 addresses so
    // that, for example, access logging will show IPv4 address format for IPv4 connections even
    // if they were received on an IPv6 socket.

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

@jrajahalme jrajahalme left a comment

Choose a reason for hiding this comment

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

Thanks for the review!

// Get the local address from the new socket if the listener is listening on IP ANY
// (e.g., 0.0.0.0 for IPv4) (local_address_ is nullptr in this case).
const Address::InstanceConstSharedPtr& local_address =
!listener->local_address_ ? listener->getLocalAddress(fd) : listener->local_address_;
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.

Address::InstanceConstSharedPtr getAnyAddress(const Address::IpVersion version) {
// There is no portable way to initialize sockaddr_in6 with a static initializer, do it with a
// helper function instead
static sockaddr_in6 v6any_() {
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.

Applied.

return Network::Utility::getIpv4AnyAddress();
}
if (v4_compat) {
static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any_(), false));
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 this:

    // This will return an IPv6 ANY address ("[::]:0") like the getIpv6AnyAddress() below, but
    // with the internal 'v6only' member set to false. This will allow a socket created from this
    // address to accept IPv4 connections. IPv4 connections received on IPv6 sockets will have
    // Ipv4-mapped IPv6 addresses, which we will then internally interpret as IPv4 addresses so
    // that, for example, access logging will show IPv4 address format for IPv4 connections even
    // if they were received on an IPv6 socket.

htuch
htuch previously approved these changes Feb 15, 2018
Copy link
Copy Markdown
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

LGTM, just need to fix_format.

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

Format fixed, sorry for the churn.

@jrajahalme
Copy link
Copy Markdown
Contributor Author

No idea why it shows I've dismissed your (stale) review. I just pushed the fix for the line you commented on.

@dnoe
Copy link
Copy Markdown
Contributor

dnoe commented Feb 15, 2018

Yeah, that's normal GitHub :) it shows dismissed stale review, because you updated a line that had a review comment so it needs to be reviewed again ¯\_(ツ)_/¯

@htuch htuch merged commit 508c7ba into envoyproxy:master Feb 15, 2018
lita pushed a commit to lita/envoy that referenced this pull request Feb 15, 2018
jpsim added a commit that referenced this pull request Nov 28, 2022
* bazel: update rules_xcodeproj to 0.8.0

* Add Hello World Objective-C example apps to the generated project
* The internal unit test labels are no longer required
* There's now an easier way to add device and simulator targets
* We can remove `archived_bundles_allowed` which was deprecated

Release info:
https://github.com/buildbuddy-io/rules_xcodeproj/releases/tag/0.8.0

Signed-off-by: JP Simard <jp@jpsim.com>
jpsim added a commit that referenced this pull request Nov 29, 2022
* bazel: update rules_xcodeproj to 0.8.0

* Add Hello World Objective-C example apps to the generated project
* The internal unit test labels are no longer required
* There's now an easier way to add device and simulator targets
* We can remove `archived_bundles_allowed` which was deprecated

Release info:
https://github.com/buildbuddy-io/rules_xcodeproj/releases/tag/0.8.0

Signed-off-by: JP Simard <jp@jpsim.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.

5 participants