Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support IPv6 addresses across all YugabyteDB servers & CLIs #3644

Closed
schoudhury opened this issue Feb 13, 2020 · 5 comments · May be fixed by #3765
Closed

Support IPv6 addresses across all YugabyteDB servers & CLIs #3644

schoudhury opened this issue Feb 13, 2020 · 5 comments · May be fixed by #3765
Assignees
Labels
area/docdb YugabyteDB core features kind/enhancement This is an enhancement of an existing feature

Comments

@schoudhury
Copy link
Contributor

schoudhury commented Feb 13, 2020

Today only IPv4 addresses are supported for all the various YugabyteDB servers and CLIs. This should be enhanced to support IPv6 addresses as well.

Some context from https://en.wikipedia.org/wiki/IPv6_address

IPv6 is the successor to the first addressing infrastructure of the Internet, Internet Protocol version 4 (IPv4). In contrast to IPv4, which defined an IP address as a 32-bit value, IPv6 addresses have a size of 128 bits. Therefore, IPv6 has a vastly enlarged address space compared to IPv4.

An IPv6 address is represented as eight groups of four hexadecimal digits, each group representing 16 bits (two octets, a group sometimes also called a hextet[6][7]). The groups are separated by colons (:). An example of an IPv6 address is: 2001:0db8:85a3:0000:0000:8a2e:0370:7334

@schoudhury schoudhury added kind/enhancement This is an enhancement of an existing feature area/docdb YugabyteDB core features labels Feb 13, 2020
@synnack
Copy link

synnack commented Feb 13, 2020

So I took a look at the source..

Looks like most of the problems can be fixed in yb/util/net/net_util.cc:

  • the AF_INET hint before getaddrinfo should go from HostToInetAddrInfo
  • HostPort::ParseString port splitting should be more intelligent for [::]:7100
  • Bind for hostnames should probably bind more sockets than one, localhost may return both 127.0.0.1 and ::1, bind to both.

Wherever there is 0.0.0.0, it should be replaced by ::, IPv6 sockets also listen on IPv4 by default (except on OpenBSD or when this is explicitly disabled, in which case you'll want to bind to multiple sockets as well, one on 0.0.0.0 and one on ::).

@rkarthik007
Copy link
Collaborator

Hi @synnack,

Awesome thanks for going through the code and pointing out the fix, we will definitely take a look at this! Also, if you're interested in contributing a fix, we'd love to work with you on enabling you. cc @iSignal @bmatican

Do you also mind adding your self to the contributors list: https://github.com/yugabyte/yugabyte-db/blob/master/CONTRIBUTORS.md

@synnack
Copy link

synnack commented Feb 26, 2020

I have something working:

diff --git a/src/yb/util/net/net_util.cc b/src/yb/util/net/net_util.cc
index 020497d6d..a48d07f68 100644
--- a/src/yb/util/net/net_util.cc
+++ b/src/yb/util/net/net_util.cc
@@ -140,24 +140,41 @@ Status HostPort::RemoveAndGetHostPortList(
   return Status::OK();
 }
 
+
+// Accepts entries like: ::1, 127.0.0.1, [::1]:7100, 0.0.0.0:7100, f.q.d.n:7100
 Status HostPort::ParseString(const string& str, uint16_t default_port) {
-  std::pair<string, string> p = strings::Split(str, strings::delimiter::Limit(":", 1));
+  uint32_t port;
 
-  // Strip any whitespace from the host.
-  StripWhiteSpace(&p.first);
+  // We look for the last colon and see if there is a valid port behind it
+  // If it looks like a valid port and there is only one colon, it's host:port
+  // If it looks like a valid port and the string starts with [ and there's a ]
+  // before the last colon, it's an IPv6 address [host]:port
+  // otherwise, just a host.
+  size_t pos = str.rfind(':');
+  if (pos != string::npos && pos > 1 && pos + 1 < str.length()
+      && SimpleAtoi(str.substr(pos + 1), &port)
+      && ((str[0] == '[' && str[pos - 1] == ']')
+          || count(str.begin(), str.end(), ':') == 1)) {
+
+    if (port == 0 || port > numeric_limits<uint16_t>::max()) {
+      return STATUS(InvalidArgument, "Invalid port", str);
+    }
 
-  // Parse the port.
-  uint32_t port;
-  if (p.second.empty() && strcount(str, ':') == 0) {
-    // No port specified.
-    port = default_port;
-  } else if (!SimpleAtoi(p.second, &port) ||
-             port > 65535) {
-    return STATUS(InvalidArgument, "Invalid port", str);
+    // Brackets indicate the address for IPv6 urls
+    if (str[0] == '[' && str[pos - 1] == ']') {
+      host_ = str.substr(1, pos - 2);
+      port_ = port;
+      return Status::OK();
+    } else {
+      host_ = str.substr(0, pos);
+      port_ = port;
+      return Status::OK();
+    }
   }
 
-  host_.swap(p.first);
-  port_ = port;
+  // Not in host:port or [host]:port format, assume it's a host
+  host_ = str;
+  port_ = default_port;
   return Status::OK();
 }
 
@@ -184,7 +201,6 @@ const string getaddrinfo_rc_to_string(int rc) {
 Result<std::unique_ptr<addrinfo, AddrinfoDeleter>> HostToInetAddrInfo(const std::string& host) {
   struct addrinfo hints;
   memset(&hints, 0, sizeof(hints));
-  hints.ai_family = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   struct addrinfo* res = nullptr;
   int rc = 0;
diff --git a/thirdparty/build_definitions/squeasel.py b/thirdparty/build_definitions/squeasel.py
index 150fdda59..ac305dd69 100644
--- a/thirdparty/build_definitions/squeasel.py
+++ b/thirdparty/build_definitions/squeasel.py
@@ -27,7 +27,7 @@ class SqueaselDependency(Dependency):
 
     def build(self, builder):
         log_prefix = builder.log_prefix(self)
-        compile_command = [builder.get_c_compiler(), '-std=c99', '-O3', '-DNDEBUG', '-fPIC', '-c',
+        compile_command = [builder.get_c_compiler(), '-std=c99', '-O3', '-DNDEBUG', '-DUSE_IPV6', '-fPIC', '-c',
                            'squeasel.c']
         compile_command += builder.compiler_flags + builder.c_flags
         log_output(log_prefix, compile_command)
diff --git a/src/yb/util/net/dns_resolver.cc b/src/yb/util/net/dns_resolver.cc
index 33f4c834e..d0b183387 100644
--- a/src/yb/util/net/dns_resolver.cc
+++ b/src/yb/util/net/dns_resolver.cc
@@ -136,12 +136,10 @@ Result<InetAddress> PickResolvedAddress(
   if (error) {
     return STATUS_FORMAT(NetworkError, "Resolve failed $0: $1", host, error.message());
   }
-  std::vector<InetAddress> addresses, addresses_v6;
+  std::vector<InetAddress> addresses;
   for (const auto& entry : entries) {
-    auto& dest = entry.endpoint().address().is_v4() ? addresses : addresses_v6;
-    dest.emplace_back(entry.endpoint().address());
+    addresses.emplace_back(entry.endpoint().address());
   }
-  addresses.insert(addresses.end(), addresses_v6.begin(), addresses_v6.end());
   if (addresses.empty()) {
     return STATUS_FORMAT(NetworkError, "No endpoints resolved for: $0", host);
   }
diff --git a/src/yb/master/master_main.cc b/src/yb/master/master_main.cc
index fa9539792..c842182c2 100644
--- a/src/yb/master/master_main.cc
+++ b/src/yb/master/master_main.cc
@@ -66,7 +66,7 @@ namespace master {
 
 static int MasterMain(int argc, char** argv) {
   // Reset some default values before parsing gflags.
-  FLAGS_rpc_bind_addresses = strings::Substitute("0.0.0.0:$0", kMasterDefaultPort);
+  FLAGS_rpc_bind_addresses = strings::Substitute("[::]:$0", kMasterDefaultPort);
   FLAGS_webserver_port = kMasterDefaultWebPort;
   FLAGS_default_memory_limit_to_ram_ratio = 0.10;
   // For masters we always want to fsync the WAL files.
diff --git a/src/yb/server/rpc_server.cc b/src/yb/server/rpc_server.cc
index be4e53d1d..5be8d9db8 100644
--- a/src/yb/server/rpc_server.cc
+++ b/src/yb/server/rpc_server.cc
@@ -57,7 +57,7 @@ using strings::Substitute;
 using std::unique_ptr;
 using std::make_unique;
 
-DEFINE_string(rpc_bind_addresses, "0.0.0.0",
+DEFINE_string(rpc_bind_addresses, "::",
               "Comma-separated list of addresses to bind to for RPC connections. "
               "Currently, ephemeral ports (i.e. port 0) are not allowed.");
 TAG_FLAG(rpc_bind_addresses, stable);
diff --git a/src/yb/server/server_base.cc b/src/yb/server/server_base.cc
index 410accf75..d58f7b3f5 100644
--- a/src/yb/server/server_base.cc
+++ b/src/yb/server/server_base.cc
@@ -107,7 +107,7 @@ using strings::Substitute;
 namespace yb {
 namespace server {
 
-static const string kWildCardHostAddress = "0.0.0.0";
+static const string kWildCardHostAddress = "::";
 
 namespace {
 
diff --git a/src/yb/server/webserver.cc b/src/yb/server/webserver.cc
index 0462637d8..ed8788ae4 100644
--- a/src/yb/server/webserver.cc
+++ b/src/yb/server/webserver.cc
@@ -98,7 +98,7 @@ Webserver::Webserver(const WebserverOptions& opts, const std::string& server_nam
   : opts_(opts),
     context_(nullptr),
     server_name_(server_name) {
-  string host = opts.bind_interface.empty() ? "0.0.0.0" : opts.bind_interface;
+  string host = opts.bind_interface.empty() ? "[::]" : opts.bind_interface;
   http_address_ = host + ":" + boost::lexical_cast<string>(opts.port);
 }
 

Parsing succeeds, listens and connects over IPv6 (this socket still also accepts IPv4 traffic):
yb-master 9266 root 32u IPv6 169841 0t0 TCP *:7100 (LISTEN)
yb-master 9266 root 36u IPv6 169843 0t0 TCP *:7000 (LISTEN)
yb-master 9266 root 40u IPv6 171198 0t0 TCP [2001:470:....]:41394->[2001:982:...]:7100 (SYN_SENT)
yb-master 9266 root 42u IPv6 171200 0t0 TCP [2001:470:...]:41398->[2001:982:...]:7100 (SYN_SENT)

Still requires a bit more testing/validation before I make a pull request, but all in all I think it is working.

@synnack
Copy link

synnack commented Feb 26, 2020

Possibly concerned about the lack of StripWhitespace in ParseString now, doesn't really seem to be the right place for it though, and did not want to modify the string from the parent.

iSignal added a commit that referenced this issue Jun 10, 2020
…nssl, curl from thirdparty

Summary:
The patch to squeasel (yugabyte/yugabyte-db-thirdparty@764fea2) changed the return type of get_bound_addresses to be sockaddr_storage that is big enough to hold both IPv4 and IPv6 addresses. This diff uses this API.

The patch also enabled IPv6 address handling in squeasel.

Test Plan:
* Verified master web ui looks good on linux dev server
* Built a release package and deployed on portal, Ran sample apps SQL and CQL workloads.
* Jenkins

Reviewers: bogdan, sergei, mikhail

Reviewed By: mikhail

Subscribers: ybase, bogdan

Differential Revision: https://phabricator.dev.yugabyte.com/D8616
iSignal added a commit that referenced this issue Jun 19, 2020
…dresses

Summary:
The primary goal of this diff is to allow yugabyte to work in IPv6-only environments without disturbing much of how it works presently in IPv4 environments. Some of the behavior in dual stack environments may still need fixes in future diffs.

1. Allow resolution to return IPv6 addresses

2. Add the ability to prefer IPv4/IPv6 addresses of certain types when multiple addresses are available. This is the case when a single domain name resolves to multiple IP addrs (via getaddrinfo, family AF_UNSPEC). This is also the case when bind to 0.0.0.0 or [::] is specified and multiple local addresses are available to advertise to other nodes (AddHostPortPBs -> GetLocalAddresses). In particular, IPv6 link local addresses like 'fe80::1%lo' which are commonly present on macbooks are not browser friendly, so added an ipv6_non_link_local filter that prefers loopback address like [::1] over [fe80::1%lo] when both are available.

3. Include HostPort parsing from the PR #3765 and fix some of the behavior within.

Test Plan:
1. Test yb-ctl on local dev server, verify IP4 behavior is unchanged.
2. Tested on dual stack nodes with both IPv4 and IPv6 addresses. Verified that when 0.0.0.0 is specified and local_address_filter, resolve_address_filter are at default values (preferring ipv4), the server binds to and advertises IPv4 interfaces. When '[::]' is specified and local_address_filter, resolve_address_filter are changed to 'ipv6_external,ipv4_non_link_local,ipv6_all', verify that the server binds to and advertises IPv6 IPs.
3. Verified binding to DNS names with local_address_filter, resolve_address_filter set to 'ipv6_external,ipv6_non_link_local,ipv6_all' that the rpc and web server bind to IPv6 IPs. pgsql_proxy_bind_address works differently with postgres binding to both IPv4 and IPv6 addresses when a DNS name with both IPv4 and IPv6 addresses is specified (because it does not understand these filter flags).
4. Added some unit tests to verify the bind address parsing and the address filtering functionality.

Reviewers: bogdan, sergei

Reviewed By: sergei

Subscribers: ybase

Differential Revision: https://phabricator.dev.yugabyte.com/D8675
deeps1991 pushed a commit to deeps1991/yugabyte-db that referenced this issue Jul 22, 2020
…s to openssl, curl from thirdparty

Summary:
The patch to squeasel (yugabyte/yugabyte-db-thirdparty@764fea2) changed the return type of get_bound_addresses to be sockaddr_storage that is big enough to hold both IPv4 and IPv6 addresses. This diff uses this API.

The patch also enabled IPv6 address handling in squeasel.

Test Plan:
* Verified master web ui looks good on linux dev server
* Built a release package and deployed on portal, Ran sample apps SQL and CQL workloads.
* Jenkins

Reviewers: bogdan, sergei, mikhail

Reviewed By: mikhail

Subscribers: ybase, bogdan

Differential Revision: https://phabricator.dev.yugabyte.com/D8616
deeps1991 pushed a commit to deeps1991/yugabyte-db that referenced this issue Jul 22, 2020
… bind addresses

Summary:
The primary goal of this diff is to allow yugabyte to work in IPv6-only environments without disturbing much of how it works presently in IPv4 environments. Some of the behavior in dual stack environments may still need fixes in future diffs.

1. Allow resolution to return IPv6 addresses

2. Add the ability to prefer IPv4/IPv6 addresses of certain types when multiple addresses are available. This is the case when a single domain name resolves to multiple IP addrs (via getaddrinfo, family AF_UNSPEC). This is also the case when bind to 0.0.0.0 or [::] is specified and multiple local addresses are available to advertise to other nodes (AddHostPortPBs -> GetLocalAddresses). In particular, IPv6 link local addresses like 'fe80::1%lo' which are commonly present on macbooks are not browser friendly, so added an ipv6_non_link_local filter that prefers loopback address like [::1] over [fe80::1%lo] when both are available.

3. Include HostPort parsing from the PR yugabyte#3765 and fix some of the behavior within.

Test Plan:
1. Test yb-ctl on local dev server, verify IP4 behavior is unchanged.
2. Tested on dual stack nodes with both IPv4 and IPv6 addresses. Verified that when 0.0.0.0 is specified and local_address_filter, resolve_address_filter are at default values (preferring ipv4), the server binds to and advertises IPv4 interfaces. When '[::]' is specified and local_address_filter, resolve_address_filter are changed to 'ipv6_external,ipv4_non_link_local,ipv6_all', verify that the server binds to and advertises IPv6 IPs.
3. Verified binding to DNS names with local_address_filter, resolve_address_filter set to 'ipv6_external,ipv6_non_link_local,ipv6_all' that the rpc and web server bind to IPv6 IPs. pgsql_proxy_bind_address works differently with postgres binding to both IPv4 and IPv6 addresses when a DNS name with both IPv4 and IPv6 addresses is specified (because it does not understand these filter flags).
4. Added some unit tests to verify the bind address parsing and the address filtering functionality.

Reviewers: bogdan, sergei

Reviewed By: sergei

Subscribers: ybase

Differential Revision: https://phabricator.dev.yugabyte.com/D8675
@iSignal
Copy link
Contributor

iSignal commented Sep 25, 2020

The functionality is checked in. I will document the net_address_filter flag needed at #5813.

@iSignal iSignal closed this as completed Sep 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/docdb YugabyteDB core features kind/enhancement This is an enhancement of an existing feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants