You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by to...@apache.org on 2017/05/18 01:10:24 UTC
[2/2] kudu git commit: KUDU-1875: Refuse unauthenticated connections
from publicly routable IP addrs
KUDU-1875: Refuse unauthenticated connections from publicly routable
IP addrs
This rejects unauthenticated connections from publicly routable IPs,
even if authentication and encryption are not configured.
An adavanced flag 'trusted_subnets' is provided to whitelist
trusted subnets. If this flag is set explicitly, all unauthenticated
or unencrypted connections are prohibited except the ones from the
specified address blocks. Otherwise, private network (127.0.0.0/8,
etc.) and local subnets of all local network interfaces will be used.
Set it to '0.0.0.0/0' allows unauthenticated/unencrypted connections
from all remote IP addresses. However, if network access is not
otherwise restricted by a firewall, malicious users may be able to
gain unauthorized access.
Change-Id: I6c3fbb5491785874c5701d6c9d866949cfac905e
Reviewed-on: http://gerrit.cloudera.org:8080/6514
Tested-by: Kudu Jenkins
Reviewed-by: Dan Burkert <da...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/c178563f
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/c178563f
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/c178563f
Branch: refs/heads/master
Commit: c178563f646f1b0e9a9a562d02a252cbe20d820d
Parents: 7dd30b1
Author: hahao <ha...@cloudera.com>
Authored: Thu Mar 30 01:08:33 2017 -0700
Committer: Dan Burkert <da...@apache.org>
Committed: Thu May 18 00:25:11 2017 +0000
----------------------------------------------------------------------
src/kudu/rpc/negotiation-test.cc | 103 +++++++++++++++++++++++++++++++-
src/kudu/rpc/server_negotiation.cc | 102 ++++++++++++++++++++++++++++++-
src/kudu/rpc/server_negotiation.h | 3 +
src/kudu/util/net/net_util-test.cc | 25 ++++++++
src/kudu/util/net/net_util.cc | 76 ++++++++++++++++++++++-
src/kudu/util/net/net_util.h | 32 +++++++++-
src/kudu/util/net/socket.h | 3 +-
7 files changed, 337 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/kudu/blob/c178563f/src/kudu/rpc/negotiation-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/rpc/negotiation-test.cc b/src/kudu/rpc/negotiation-test.cc
index 329a2b5..68185bb 100644
--- a/src/kudu/rpc/negotiation-test.cc
+++ b/src/kudu/rpc/negotiation-test.cc
@@ -122,6 +122,8 @@ struct NegotiationDescriptor {
EndpointConfig client;
EndpointConfig server;
+ bool use_test_socket;
+
bool rpc_encrypt_loopback;
// The expected client status from negotiating.
@@ -146,6 +148,14 @@ std::ostream& operator<<(std::ostream& o, NegotiationDescriptor c) {
return o;
}
+class NegotiationTestSocket : public Socket {
+ public:
+ // Return an arbitrary public IP
+ Status GetPeerAddress(Sockaddr *cur_addr) const override {
+ return cur_addr->ParseString("8.8.8.8:12345", 0);
+ }
+};
+
class TestNegotiation : public RpcTestBase,
public ::testing::WithParamInterface<NegotiationDescriptor> {
public:
@@ -207,7 +217,10 @@ TEST_P(TestNegotiation, TestNegotiation) {
ASSERT_OK(client_socket->Init(0));
client_socket->Connect(server_addr);
- unique_ptr<Socket> server_socket(new Socket());
+ unique_ptr<Socket> server_socket(desc.use_test_socket ?
+ new NegotiationTestSocket() :
+ new Socket());
+
Sockaddr client_addr;
CHECK_OK(listening_socket.Accept(server_socket.get(), &client_addr, 0));
@@ -270,9 +283,20 @@ TEST_P(TestNegotiation, TestNegotiation) {
Status client_status;
Status server_status;
thread client_thread([&] () {
+ scoped_refptr<Trace> t(new Trace());
+ ADOPT_TRACE(t.get());
client_status = client_negotiation.Negotiate();
// Close the socket so that the server will not block forever on error.
client_negotiation.socket()->Close();
+
+ if (FLAGS_rpc_trace_negotiation || !client_status.ok()) {
+ string msg = Trace::CurrentTrace()->DumpToString();
+ if (!client_status.ok()) {
+ LOG(WARNING) << "Failed client RPC negotiation. Client trace:\n" << msg;
+ } else {
+ LOG(INFO) << "RPC negotiation tracing enabled. Client trace:\n" << msg;
+ }
+ }
});
thread server_thread([&] () {
scoped_refptr<Trace> t(new Trace());
@@ -284,9 +308,9 @@ TEST_P(TestNegotiation, TestNegotiation) {
if (FLAGS_rpc_trace_negotiation || !server_status.ok()) {
string msg = Trace::CurrentTrace()->DumpToString();
if (!server_status.ok()) {
- LOG(WARNING) << "Failed RPC negotiation. Trace:\n" << msg;
+ LOG(WARNING) << "Failed server RPC negotiation. Server trace:\n" << msg;
} else {
- LOG(INFO) << "RPC negotiation tracing enabled. Trace:\n" << msg;
+ LOG(INFO) << "RPC negotiation tracing enabled. Server trace:\n" << msg;
}
}
});
@@ -367,6 +391,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::NotAuthorized(".*client is not configured with an authentication type"),
Status::NetworkError(""),
AuthenticationType::INVALID,
@@ -390,6 +415,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::NotAuthorized(".* server mechanism list is empty"),
Status::NotAuthorized(".* server mechanism list is empty"),
AuthenticationType::INVALID,
@@ -413,6 +439,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::DISABLED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -436,6 +463,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::DISABLED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -459,6 +487,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::DISABLED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -482,6 +511,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::DISABLED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -505,6 +535,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::DISABLED,
},
false,
+ false,
Status::NotAuthorized(".*client does not have Kerberos enabled"),
Status::NetworkError(""),
AuthenticationType::INVALID,
@@ -528,6 +559,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
false,
RpcEncryption::OPTIONAL,
},
+ false,
true,
Status::OK(),
Status::OK(),
@@ -554,6 +586,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -577,6 +610,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -600,6 +634,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::CERTIFICATE,
@@ -623,6 +658,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::TOKEN,
@@ -649,6 +685,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -672,6 +709,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::OPTIONAL,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::CERTIFICATE,
@@ -695,6 +733,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::REQUIRED,
},
false,
+ false,
Status::NotAuthorized(".*client does not support required TLS encryption"),
Status::NotAuthorized(".*client does not support required TLS encryption"),
AuthenticationType::SASL,
@@ -718,6 +757,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::DISABLED,
},
false,
+ false,
Status::NotAuthorized(".*server does not support required TLS encryption"),
Status::NetworkError(""),
AuthenticationType::SASL,
@@ -741,6 +781,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::REQUIRED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -764,6 +805,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::REQUIRED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -787,6 +829,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::REQUIRED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -810,6 +853,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::REQUIRED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
@@ -833,6 +877,7 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::REQUIRED,
},
false,
+ false,
Status::NotAuthorized(".*client does not support required TLS encryption"),
Status::NotAuthorized(".*client does not support required TLS encryption"),
AuthenticationType::SASL,
@@ -856,11 +901,63 @@ INSTANTIATE_TEST_CASE_P(NegotiationCombinations,
RpcEncryption::REQUIRED,
},
false,
+ false,
Status::OK(),
Status::OK(),
AuthenticationType::SASL,
SaslMechanism::GSSAPI,
true,
+ },
+
+ // client: PLAIN
+ // server: PLAIN
+ // connection from public routable IP
+ NegotiationDescriptor {
+ EndpointConfig {
+ PkiConfig::NONE,
+ { SaslMechanism::PLAIN },
+ false,
+ RpcEncryption::OPTIONAL
+ },
+ EndpointConfig {
+ PkiConfig::NONE,
+ { SaslMechanism::PLAIN },
+ false,
+ RpcEncryption::OPTIONAL
+ },
+ true,
+ false,
+ Status::NotAuthorized(".*unencrypted connections from publicly routable IPs"),
+ Status::NotAuthorized(".*unencrypted connections from publicly routable IPs"),
+ AuthenticationType::SASL,
+ SaslMechanism::PLAIN,
+ false,
+ },
+
+ // client: GSSAPI, TLS required, externally-signed cert
+ // server: GSSAPI, TLS required, externally-signed cert
+ // connection from public routable IP
+ NegotiationDescriptor {
+ EndpointConfig {
+ PkiConfig::EXTERNALLY_SIGNED,
+ { SaslMechanism::GSSAPI },
+ false,
+ RpcEncryption::REQUIRED,
+ },
+ EndpointConfig {
+ PkiConfig::EXTERNALLY_SIGNED,
+ { SaslMechanism::GSSAPI },
+ false,
+ RpcEncryption::REQUIRED,
+ },
+ true,
+ // true as no longer a loopback connection.
+ true,
+ Status::OK(),
+ Status::OK(),
+ AuthenticationType::SASL,
+ SaslMechanism::GSSAPI,
+ true,
}
));
http://git-wip-us.apache.org/repos/asf/kudu/blob/c178563f/src/kudu/rpc/server_negotiation.cc
----------------------------------------------------------------------
diff --git a/src/kudu/rpc/server_negotiation.cc b/src/kudu/rpc/server_negotiation.cc
index 7cebf8a..5e6d070 100644
--- a/src/kudu/rpc/server_negotiation.cc
+++ b/src/kudu/rpc/server_negotiation.cc
@@ -30,6 +30,7 @@
#include "kudu/gutil/casts.h"
#include "kudu/gutil/endian.h"
#include "kudu/gutil/map-util.h"
+#include "kudu/gutil/strings/split.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/rpc/blocking_ops.h"
#include "kudu/rpc/constants.h"
@@ -45,6 +46,7 @@
#include "kudu/util/fault_injection.h"
#include "kudu/util/flag_tags.h"
#include "kudu/util/logging.h"
+#include "kudu/util/net/net_util.h"
#include "kudu/util/net/sockaddr.h"
#include "kudu/util/net/socket.h"
#include "kudu/util/scoped_cleanup.h"
@@ -65,10 +67,45 @@ TAG_FLAG(rpc_inject_invalid_authn_token_ratio, unsafe);
DECLARE_bool(rpc_encrypt_loopback_connections);
+DEFINE_string(trusted_subnets,
+ "127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16",
+ "A trusted subnet whitelist. If set explicitly, all unauthenticated "
+ "or unencrypted connections are prohibited except the ones from the "
+ "specified address blocks. Otherwise, private network (127.0.0.0/8, etc.) "
+ "and local subnets of all local network interfaces will be used. Set it "
+ "to '0.0.0.0/0' to allow unauthenticated/unencrypted connections from all "
+ "remote IP addresses. However, if network access is not otherwise restricted "
+ "by a firewall, malicious users may be able to gain unauthorized access.");
+TAG_FLAG(trusted_subnets, advanced);
+TAG_FLAG(trusted_subnets, evolving);
+
+static bool ValidateTrustedSubnets(const char* /*flagname*/, const string& value) {
+ if (value.empty()) {
+ return true;
+ }
+
+ for (const auto& t : strings::Split(value, ",", strings::SkipEmpty())) {
+ kudu::Network network;
+ kudu::Status s = network.ParseCIDRString(t.ToString());
+ if (!s.ok()) {
+ LOG(ERROR) << "Invalid subnet address: " << t
+ << ". Subnet must be specified in CIDR notation.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+DEFINE_validator(trusted_subnets, &ValidateTrustedSubnets);
namespace kudu {
namespace rpc {
+namespace {
+vector<Network>* g_trusted_subnets = nullptr;
+} // anonymous namespace
+
static int ServerNegotiationGetoptCb(ServerNegotiation* server_negotiation,
const char* plugin_name,
const char* option,
@@ -174,6 +211,30 @@ Status ServerNegotiation::Negotiate() {
tls_negotiated_ = true;
}
+ // Rejects any connection from public routable IPs if encryption
+ // is disabled. See KUDU-1875.
+ if (!tls_negotiated_) {
+ Sockaddr addr;
+ RETURN_NOT_OK(socket_->GetPeerAddress(&addr));
+
+ if (!IsTrustedConnection(addr)) {
+ // Receives client response before sending error
+ // message, even though the response is never used,
+ // to avoid risk condition that connection gets
+ // closed before client receives server's error
+ // message.
+ NegotiatePB request;
+ RETURN_NOT_OK(RecvNegotiatePB(&request, &recv_buf));
+
+ Status s = Status::NotAuthorized("unencrypted connections from publicly routable "
+ "IPs are prohibited. See --trusted_subnets flag "
+ "for more information.",
+ addr.ToString());
+ RETURN_NOT_OK(SendError(ErrorStatusPB::FATAL_UNAUTHORIZED, s));
+ return s;
+ }
+ }
+
// Step 4: Authentication
switch (negotiated_authn_) {
case AuthenticationType::SASL:
@@ -284,7 +345,7 @@ Status ServerNegotiation::SendError(ErrorStatusPB::RpcErrorCodePB code, const St
msg.set_code(code);
msg.set_message(err.ToString());
- TRACE("Sending RPC error: $0", ErrorStatusPB::RpcErrorCodePB_Name(code));
+ TRACE("Sending RPC error: $0: $1", ErrorStatusPB::RpcErrorCodePB_Name(code), err.ToString());
RETURN_NOT_OK(SendFramedMessageBlocking(socket(), header, msg, deadline_));
return Status::OK();
@@ -702,6 +763,22 @@ Status ServerNegotiation::HandleSaslInitiate(const NegotiatePB& request) {
negotiated_mech_ = SaslMechanism::value_of(mechanism);
+ // Rejects any connection from public routable IPs if authentication mechanism
+ // is plain. See KUDU-1875.
+ if (negotiated_mech_ == SaslMechanism::PLAIN) {
+ Sockaddr addr;
+ RETURN_NOT_OK(socket_->GetPeerAddress(&addr));
+
+ if (!IsTrustedConnection(addr)) {
+ Status s = Status::NotAuthorized("unauthenticated connections from publicly "
+ "routable IPs are prohibited. See "
+ "--trusted_subnets flag for more information.",
+ addr.ToString());
+ RETURN_NOT_OK(SendError(ErrorStatusPB::FATAL_UNAUTHORIZED, s));
+ return s;
+ }
+ }
+
// If the negotiated mechanism is GSSAPI (Kerberos), configure SASL to use
// integrity protection so that the channel bindings and nonce can be
// verified.
@@ -876,5 +953,28 @@ int ServerNegotiation::PlainAuthCb(sasl_conn_t* /*conn*/,
return SASL_OK;
}
+bool ServerNegotiation::IsTrustedConnection(const Sockaddr& addr) {
+ static std::once_flag once;
+ std::call_once(once, [] {
+ g_trusted_subnets = new vector<Network>();
+ CHECK_OK(Network::ParseCIDRStrings(FLAGS_trusted_subnets, g_trusted_subnets));
+
+ // If --trusted_subnets is not set explicitly, local subnets of all local network
+ // interfaces as well as the default private subnets will be used.
+ if (google::GetCommandLineFlagInfoOrDie("trusted_subnets").is_default) {
+ std::vector<Network> local_networks;
+ WARN_NOT_OK(GetLocalNetworks(&local_networks),
+ "Unable to get local networks.");
+
+ g_trusted_subnets->insert(g_trusted_subnets->end(),
+ local_networks.begin(),
+ local_networks.end());
+ }
+ });
+
+ return std::any_of(g_trusted_subnets->begin(), g_trusted_subnets->end(),
+ [&](const Network& t) { return t.WithinNetwork(addr); });
+}
+
} // namespace rpc
} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/c178563f/src/kudu/rpc/server_negotiation.h
----------------------------------------------------------------------
diff --git a/src/kudu/rpc/server_negotiation.h b/src/kudu/rpc/server_negotiation.h
index 07e0057..e9e945a 100644
--- a/src/kudu/rpc/server_negotiation.h
+++ b/src/kudu/rpc/server_negotiation.h
@@ -204,6 +204,9 @@ class ServerNegotiation {
// Receive and validate the ConnectionContextPB.
Status RecvConnectionContext(faststring* recv_buf) WARN_UNUSED_RESULT;
+ // Returns true if connection is from trusted subnets or local networks.
+ bool IsTrustedConnection(const Sockaddr& addr);
+
// The socket to the remote client.
std::unique_ptr<Socket> socket_;
http://git-wip-us.apache.org/repos/asf/kudu/blob/c178563f/src/kudu/util/net/net_util-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/net/net_util-test.cc b/src/kudu/util/net/net_util-test.cc
index b1c33ef..c77b054 100644
--- a/src/kudu/util/net/net_util-test.cc
+++ b/src/kudu/util/net/net_util-test.cc
@@ -92,6 +92,31 @@ TEST_F(NetUtilTest, TestResolveAddresses) {
ASSERT_OK(hp.ResolveAddresses(nullptr));
}
+TEST_F(NetUtilTest, TestWithinNetwork) {
+ Sockaddr addr;
+ Network network;
+
+ ASSERT_OK(addr.ParseString("10.0.23.0:12345", 0));
+ ASSERT_OK(network.ParseCIDRString("10.0.0.0/8"));
+ EXPECT_TRUE(network.WithinNetwork(addr));
+
+ ASSERT_OK(addr.ParseString("172.28.3.4:0", 0));
+ ASSERT_OK(network.ParseCIDRString("172.16.0.0/12"));
+ EXPECT_TRUE(network.WithinNetwork(addr));
+
+ ASSERT_OK(addr.ParseString("192.168.0.23", 0));
+ ASSERT_OK(network.ParseCIDRString("192.168.1.14/16"));
+ EXPECT_TRUE(network.WithinNetwork(addr));
+
+ ASSERT_OK(addr.ParseString("8.8.8.8:0", 0));
+ ASSERT_OK(network.ParseCIDRString("0.0.0.0/0"));
+ EXPECT_TRUE(network.WithinNetwork(addr));
+
+ ASSERT_OK(addr.ParseString("192.169.0.23", 0));
+ ASSERT_OK(network.ParseCIDRString("192.168.0.0/16"));
+ EXPECT_FALSE(network.WithinNetwork(addr));
+}
+
// Ensure that we are able to do a reverse DNS lookup on various IP addresses.
// The reverse lookups should never fail, but may return numeric strings.
TEST_F(NetUtilTest, TestReverseLookup) {
http://git-wip-us.apache.org/repos/asf/kudu/blob/c178563f/src/kudu/util/net/net_util.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/net/net_util.cc b/src/kudu/util/net/net_util.cc
index e1f26cb..ac5c67f 100644
--- a/src/kudu/util/net/net_util.cc
+++ b/src/kudu/util/net/net_util.cc
@@ -16,9 +16,10 @@
// under the License.
#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <netdb.h>
#include <algorithm>
#include <boost/functional/hash.hpp>
@@ -27,6 +28,7 @@
#include <utility>
#include <vector>
+#include "kudu/gutil/endian.h"
#include "kudu/gutil/gscoped_ptr.h"
#include "kudu/gutil/map-util.h"
#include "kudu/gutil/strings/join.h"
@@ -41,6 +43,7 @@
#include "kudu/util/flag_tags.h"
#include "kudu/util/net/net_util.h"
#include "kudu/util/net/sockaddr.h"
+#include "kudu/util/scoped_cleanup.h"
#include "kudu/util/stopwatch.h"
#include "kudu/util/subprocess.h"
#include "kudu/util/trace.h"
@@ -173,6 +176,50 @@ string HostPort::ToCommaSeparatedString(const vector<HostPort>& hostports) {
return JoinStrings(hostport_strs, ",");
}
+Network::Network()
+ : addr_(0),
+ netmask_(0) {
+}
+
+Network::Network(uint32_t addr, uint32_t netmask)
+ : addr_(addr), netmask_(netmask) {}
+
+bool Network::WithinNetwork(const Sockaddr& addr) const {
+ return ((addr.addr().sin_addr.s_addr & netmask_) ==
+ (addr_ & netmask_));
+}
+
+Status Network::ParseCIDRString(const string& addr) {
+ std::pair<string, string> p = strings::Split(addr, strings::delimiter::Limit("/", 1));
+
+ kudu::Sockaddr sockaddr;
+ Status s = sockaddr.ParseString(p.first, 0);
+
+ uint32_t bits;
+ bool success = SimpleAtoi(p.second, &bits);
+
+ if (!s.ok() || !success || bits > 32) {
+ return Status::NetworkError("Unable to parse CIDR address", addr);
+ }
+
+ // Netmask in network byte order
+ uint32_t netmask = NetworkByteOrder::FromHost32(~(0xffffffff >> bits));
+ addr_ = sockaddr.addr().sin_addr.s_addr;
+ netmask_ = netmask;
+ return Status::OK();
+}
+
+Status Network::ParseCIDRStrings(const string& comma_sep_addrs,
+ vector<Network>* res) {
+ vector<string> addr_strings = strings::Split(comma_sep_addrs, ",", strings::SkipEmpty());
+ for (const string& addr_string : addr_strings) {
+ Network network;
+ RETURN_NOT_OK(network.ParseCIDRString(addr_string));
+ res->push_back(network);
+ }
+ return Status::OK();
+}
+
bool IsPrivilegedPort(uint16_t port) {
return port <= 1024 && port != 0;
}
@@ -215,6 +262,33 @@ Status GetHostname(string* hostname) {
return Status::OK();
}
+Status GetLocalNetworks(std::vector<Network>* net) {
+ struct ifaddrs *ifap = nullptr;
+
+ int ret = getifaddrs(&ifap);
+ auto cleanup = MakeScopedCleanup([&]() {
+ if (ifap) freeifaddrs(ifap);
+ });
+
+ if (ret != 0) {
+ return Status::NetworkError("Unable to determine local network addresses",
+ ErrnoToString(errno),
+ errno);
+ }
+
+ net->clear();
+ for (struct ifaddrs *ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ Sockaddr addr(*reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr));
+ Sockaddr mask(*reinterpret_cast<struct sockaddr_in*>(ifa->ifa_netmask));
+ Network network(addr.addr().sin_addr.s_addr, mask.addr().sin_addr.s_addr);
+ net->push_back(network);
+ }
+ }
+
+ return Status::OK();
+}
+
Status GetFQDN(string* hostname) {
TRACE_EVENT0("net", "GetFQDN");
// Start with the non-qualified hostname
http://git-wip-us.apache.org/repos/asf/kudu/blob/c178563f/src/kudu/util/net/net_util.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/net/net_util.h b/src/kudu/util/net/net_util.h
index 863d0f3..b246c5e 100644
--- a/src/kudu/util/net/net_util.h
+++ b/src/kudu/util/net/net_util.h
@@ -91,6 +91,33 @@ struct HostPortEqualityPredicate {
}
};
+// A container for addr:mask pair.
+// Both addr and netmask are in big-endian byte order
+// (same as network byte order).
+class Network {
+ public:
+ Network();
+ Network(uint32_t addr, uint32_t netmask);
+
+ uint32_t addr() const { return addr_; }
+
+ uint32_t netmask() const { return netmask_; }
+
+ // Returns true if the address is within network.
+ bool WithinNetwork(const Sockaddr& addr) const;
+
+ // Parses a "addr/netmask" (CIDR notation) pair into this object.
+ Status ParseCIDRString(const std::string& addr);
+
+ // Parses a comma separated list of "addr/netmask" (CIDR notation)
+ // pairs into a vector of Network objects.
+ static Status ParseCIDRStrings(
+ const std::string& comma_sep_addrs, std::vector<Network>* res);
+ private:
+ uint32_t addr_;
+ uint32_t netmask_;
+};
+
// Parse and resolve the given comma-separated list of addresses.
//
// The resulting addresses will be resolved, made unique, and added to
@@ -107,8 +134,11 @@ bool IsPrivilegedPort(uint16_t port);
// Return the local machine's hostname.
Status GetHostname(std::string* hostname);
+// Returns local subnets of all local network interfaces.
+Status GetLocalNetworks(std::vector<Network>* net);
+
// Return the local machine's FQDN.
-Status GetFQDN(std::string* fqdn);
+Status GetFQDN(std::string* hostname);
// Returns a single socket address from a HostPort.
// If the hostname resolves to multiple addresses, returns the first in the
http://git-wip-us.apache.org/repos/asf/kudu/blob/c178563f/src/kudu/util/net/socket.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/net/socket.h b/src/kudu/util/net/socket.h
index 1362e67..ce5b7bb 100644
--- a/src/kudu/util/net/socket.h
+++ b/src/kudu/util/net/socket.h
@@ -97,7 +97,8 @@ class Socket {
Status GetSocketAddress(Sockaddr *cur_addr) const;
// Call getpeername to get the address of the connected peer.
- Status GetPeerAddress(Sockaddr *cur_addr) const;
+ // It is virtual so that tests can override.
+ virtual Status GetPeerAddress(Sockaddr *cur_addr) const;
// Return true if this socket is determined to be a loopback connection
// (i.e. the local and remote peer share an IP address).