listener: Use v4-mapped addressing consistently.#2572
listener: Use v4-mapped addressing consistently.#2572htuch merged 6 commits intoenvoyproxy:masterfrom
Conversation
mattklein123
left a comment
There was a problem hiding this comment.
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>
17b940d to
59b1077
Compare
|
Added a test case that fails without the fix. |
|
@zuercher can you take a first pass on this? Thanks. |
source/common/network/utility.cc
Outdated
| // 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_(); |
There was a problem hiding this comment.
I think you can just inline this call.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
| TEST_P(ListenerImplTest, WildcardListenerIpv4Compat) { | ||
| Stats::IsolatedStoreImpl stats_store; | ||
| Event::DispatcherImpl dispatcher; | ||
| Network::TcpListenSocket socket(Network::Test::getAnyAddress(version_, true), true); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 ([::]).
source/common/network/utility.h
Outdated
| * IPv6 address are to be accepted. | ||
| */ | ||
| static Address::InstanceConstSharedPtr getIpv6AnyAddress(); | ||
| static Address::InstanceConstSharedPtr getIpv6AnyAddress(bool v6only = true); |
There was a problem hiding this comment.
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?
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>
zuercher
left a comment
There was a problem hiding this comment.
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 |
test/test_common/network_utility.cc
Outdated
| // 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 = {}; |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
Done, had to change the function to return the struct to not return a reference to the stack.
test/test_common/network_utility.cc
Outdated
| } | ||
| if (v4_compat) { | ||
| static sockaddr_in6 v6any = v6any_(); | ||
| static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any, false)); |
There was a problem hiding this comment.
I would just call v6any_() on this line instead of initializing an extra static var. TIOLI.
Signed-off-by: Jarno Rajahalme <jarno@covalent.io>
6a59c2a to
c23d305
Compare
jrajahalme
left a comment
There was a problem hiding this comment.
Thanks for the review!
test/test_common/network_utility.cc
Outdated
| // 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 = {}; |
There was a problem hiding this comment.
Done, had to change the function to return the struct to not return a reference to the stack.
test/test_common/network_utility.cc
Outdated
| } | ||
| if (v4_compat) { | ||
| static sockaddr_in6 v6any = v6any_(); | ||
| static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any, false)); |
|
|
||
| // 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 |
|
Sorry, I read the wrong list of names. @htuch I think this is ready for you to look at. |
htuch
left a comment
There was a problem hiding this comment.
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_; |
There was a problem hiding this comment.
Tiny nit: prefer to negate the conditional here, easier to parse.
test/test_common/network_utility.cc
Outdated
| 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_() { |
There was a problem hiding this comment.
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.
test/test_common/network_utility.cc
Outdated
| return Network::Utility::getIpv4AnyAddress(); | ||
| } | ||
| if (v4_compat) { | ||
| static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any_(), false)); |
There was a problem hiding this comment.
Can you add some internal comments here explaining how this (maybe using ASCII address notation) differs from Network::Utility::getIpv6AnyAddress() below?
There was a problem hiding this comment.
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>
jrajahalme
left a comment
There was a problem hiding this comment.
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_; |
test/test_common/network_utility.cc
Outdated
| 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_() { |
test/test_common/network_utility.cc
Outdated
| return Network::Utility::getIpv4AnyAddress(); | ||
| } | ||
| if (v4_compat) { | ||
| static Address::InstanceConstSharedPtr any(new Address::Ipv6Instance(v6any_(), false)); |
There was a problem hiding this comment.
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>
|
Format fixed, sorry for the churn. |
|
No idea why it shows I've dismissed your (stale) review. I just pushed the fix for the line you commented on. |
|
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 |
* 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>
* 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>
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