Skip to content

address: IPv4/IPv6 dual bind.#2201

Merged
htuch merged 10 commits intoenvoyproxy:masterfrom
htuch:ipv46-bind
Jan 3, 2018
Merged

address: IPv4/IPv6 dual bind.#2201
htuch merged 10 commits intoenvoyproxy:masterfrom
htuch:ipv46-bind

Conversation

@htuch
Copy link
Copy Markdown
Member

@htuch htuch commented Dec 13, 2017

This adds support for listening for both IPv4 and IPv6 when binding to ::.

Risk Level: Low
Testing: Added unit tests for resolver and address implementations. No integration tests yet, need to convert the admin integration test to v2 first to allow for ipv4_compat to be set on the admin bind address.
API Changes: envoyproxy/data-plane-api#332

Signed-off-by: Harvey Tuch [email protected]

This adds support for listening for both IPv4 and IPv6 when binding to ::.

Signed-off-by: Harvey Tuch <[email protected]>
@htuch
Copy link
Copy Markdown
Member Author

htuch commented Dec 13, 2017

Feel free to review, I will work on an independent PR to convert the admin integration test to v2 so I can get the integration tests happening.

@alyssawilk
Copy link
Copy Markdown
Contributor

FWIW I have that test 90% ported but the patch depends on 2185 landing

/**
* @return IPv4-IPv6 mapping disabled for IPv6 addresses?
*/
virtual bool v6only() const PURE;
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.

Should this be on class Ipv6 instead of here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe? Need to do some dynamic casting to make use of where I needed it, but I agree it's cleaner at the interface level.

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't you pivot off of ipv6() being not-null in the cases that you need?

}

InstanceConstSharedPtr addressFromFd(int fd) {
InstanceConstSharedPtr addressFromFd(int fd, bool v6only) {
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.

In this case, shouldn't we getsockopt to determine if it is set, instead of having this as a parameter?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep, this makes sense.

int socket_v6only = 0;
socklen_t size_int = sizeof(socket_v6only);
rc = ::getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &socket_v6only, &size_int);
if (rc != 0 && errno != EOPNOTSUPP) {
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.

Is this safe on all platforms, ie do all platforms that don't support this option return EOPNOTSUPP? As much as it is distasteful, it's tempting to ignore all errors from this getsockopt and just assume the default value if it fails.

int socket_v6only = 0;
socklen_t size_int = sizeof(socket_v6only);
rc = ::getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &socket_v6only, &size_int);
if (rc != 0 && errno != EOPNOTSUPP) {
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.

On second thought, there is a RELEASE_ASSERT that setting this sockopt does not fail, so I think you can treat any failure (ie ignore value of errno) as a failure here.

@htuch htuch dismissed ggreenway’s stale review December 27, 2017 02:22

Changes made.

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.

LGTM, small question.

if (ss.ss_family == AF_INET6) {
socklen_t size_int = sizeof(socket_v6only);
rc = ::getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &socket_v6only, &size_int);
if (rc != 0) {
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.

realistically can this ever fail? Should this be RELEASE_ASSERT?

@htuch htuch merged commit 12101e5 into envoyproxy:master Jan 3, 2018
@htuch htuch deleted the ipv46-bind branch January 3, 2018 11:38
@jrajahalme
Copy link
Copy Markdown
Contributor

AFAIK this represents IPv4 connections received on a [::] listener as having (IPv4 compatible) IPv6 addresses. I have an implementation on my work queue that returns an Address::Ipv4Instance instead in that case. I thought otherwise logging and possibly listener filter chain address matching would be impacted.

Also, I would have assumed risk of existing deployments breaking due to v6 sockets accepting v4 connections being low, especially if the address would be internally treated as an IPv4 address like above. If this assumption holds there would be no need to do V6ONLY syscalls at all on platforms that default to accepting v4 connections on v6 sockets.

Thoughs?

@htuch
Copy link
Copy Markdown
Member Author

htuch commented Jan 3, 2018

I think for listener filter chain address matching, you just use the IPv4-in-IPv6 encoding in your configs and you're fine. The idea being you use the correct IP address representation matching what you are binding the listener to. For logging, I can see some advantage in either using an Ipv4Instance or just having an Address::Instance representation function that can do the IPv4 representation.

If you have an implementation already in-flight, maybe post a rough PR and we can discuss? Otherwise I would be inclined to consider whether what we have there today is good enough.

We agreed in envoyproxy/data-plane-api#332 to follow the haproxy convention of explicitly calling out dual bind in the listener socket options, is there a reason to change this?

@jrajahalme
Copy link
Copy Markdown
Contributor

You can see my patch here, very simple but needs rebasing after these changes:

jrajahalme@baa4785

@jrajahalme
Copy link
Copy Markdown
Contributor

jrajahalme commented Jan 3, 2018

As for the explicit dual bind, I had not thought it is necessary, but have no reason to request a change to the agreed solution. Apart from code complexity, but that is in the eye of the beholder anyway :-)

@jrajahalme
Copy link
Copy Markdown
Contributor

jrajahalme commented Jan 3, 2018 via email

@jrajahalme
Copy link
Copy Markdown
Contributor

jrajahalme commented Jan 4, 2018 via email

@htuch
Copy link
Copy Markdown
Member Author

htuch commented Jan 4, 2018

@jrajahalme I think there are some valid reasons for explicit dual bind; you might want to allow multiple processes to listen on the same port and have one process pick up the IPv4 incoming and the other the IPv6. Having the ability to decide whether you are listening only for IPv6 becomes useful to allow that to happen. So, yeah, I think we should keep that.

Thanks for #2309, we can continue this thread there.

jpsim added a commit that referenced this pull request Nov 28, 2022
Adding changes since 0.4.5 to release notes

Signed-off-by: JP Simard <[email protected]>
jpsim added a commit that referenced this pull request Nov 29, 2022
Adding changes since 0.4.5 to release notes

Signed-off-by: JP Simard <[email protected]>
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