Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions envoy/api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ envoy_cc_library(
"os_sys_calls_hot_restart.h",
"os_sys_calls_linux.h",
],
external_deps = ["abseil_optional"],
deps = [
"//envoy/network:address_interface",
],
)
44 changes: 44 additions & 0 deletions envoy/api/os_sys_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
#include <chrono>
#include <memory>
#include <string>
#include <vector>

#include "envoy/api/os_sys_calls_common.h"
#include "envoy/common/platform.h"
#include "envoy/common/pure.h"
#include "envoy/network/address.h"

#include "absl/types/optional.h"

namespace Envoy {
namespace Api {
Expand All @@ -17,6 +21,23 @@ struct EnvoyTcpInfo {
std::chrono::microseconds tcpi_rtt;
};

// Small struct to avoid exposing ifaddrs -- which is not defined in all platforms -- to the
// codebase.
struct InterfaceAddress {
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.

happy to move this to address.h if we think it makes more sense there. I couldn't decide.

InterfaceAddress(absl::string_view interface_name, unsigned int interface_flags,
Envoy::Network::Address::InstanceConstSharedPtr interface_addr)
: interface_name_(interface_name), interface_flags_(interface_flags),
interface_addr_(interface_addr) {}

std::string interface_name_;
unsigned int interface_flags_;
Envoy::Network::Address::InstanceConstSharedPtr interface_addr_;
};

using InterfaceAddressVector = std::vector<InterfaceAddress>;

using AlternateGetifaddrs = std::function<SysCallIntResult(InterfaceAddressVector& interfaces)>;

class OsSysCalls {
public:
virtual ~OsSysCalls() = default;
Expand Down Expand Up @@ -195,6 +216,29 @@ class OsSysCalls {
* @see man TCP_INFO. Get the tcp info for the socket.
*/
virtual SysCallBoolResult socketTcpInfo(os_fd_t sockfd, EnvoyTcpInfo* tcp_info) PURE;

/**
* return true if the OS supports getifaddrs.
*/
virtual bool supportsGetifaddrs() const PURE;

/**
* @see man getifaddrs
*/
virtual SysCallIntResult getifaddrs(InterfaceAddressVector& interfaces) PURE;

/**
* allows a platform to override getifaddrs or provide an implementation if one does not exist
* natively.
*
* @arg alternate_getifaddrs function pointer to implementation.
*/
virtual void setAlternateGetifaddrs(AlternateGetifaddrs alternate_getifaddrs) {
alternate_getifaddrs_ = alternate_getifaddrs;
}

protected:
absl::optional<AlternateGetifaddrs> alternate_getifaddrs_{};
};

using OsSysCallsPtr = std::unique_ptr<OsSysCalls>;
Expand Down
12 changes: 0 additions & 12 deletions envoy/common/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,18 +299,6 @@ struct mmsghdr {
};
#endif

#define SUPPORTS_GETIFADDRS
#ifdef WIN32
#undef SUPPORTS_GETIFADDRS
#endif

// https://android.googlesource.com/platform/prebuilts/ndk/+/dev/platform/sysroot/usr/include/ifaddrs.h
#ifdef __ANDROID_API__
#if __ANDROID_API__ < 24
#undef SUPPORTS_GETIFADDRS
#endif // __ANDROID_API__ < 24
#endif // ifdef __ANDROID_API__

// TODO: Remove once bazel supports NDKs > 21
#define SUPPORTS_CPP_17_CONTIGUOUS_ITERATOR
#ifdef __ANDROID_API__
Expand Down
3 changes: 0 additions & 3 deletions envoy/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ envoy_package()
envoy_cc_library(
name = "address_interface",
hdrs = ["address.h"],
deps = [
"//envoy/api:os_sys_calls_interface",
],
)

envoy_cc_library(
Expand Down
1 change: 0 additions & 1 deletion envoy/network/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <memory>
#include <string>

#include "envoy/api/os_sys_calls.h"
#include "envoy/common/platform.h"
#include "envoy/common/pure.h"

Expand Down
1 change: 1 addition & 0 deletions envoy/network/io_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <memory>

#include "envoy/api/io_error.h"
#include "envoy/api/os_sys_calls_common.h"
#include "envoy/common/platform.h"
#include "envoy/common/pure.h"
#include "envoy/event/file_event.h"
Expand Down
1 change: 1 addition & 0 deletions source/common/api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ envoy_cc_library(
}),
deps = [
"//envoy/api:os_sys_calls_interface",
"//source/common/network:address_lib",
"//source/common/singleton:threadsafe_singleton",
],
)
64 changes: 64 additions & 0 deletions source/common/api/posix/os_sys_calls_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>

#include "source/common/api/os_sys_calls_impl.h"
#include "source/common/network/address_impl.h"

namespace Envoy {
namespace Api {
Expand Down Expand Up @@ -296,5 +297,68 @@ SysCallBoolResult OsSysCallsImpl::socketTcpInfo([[maybe_unused]] os_fd_t sockfd,
return {false, EOPNOTSUPP};
}

bool OsSysCallsImpl::supportsGetifaddrs() const {
// TODO: eliminate this branching by upstreaming an alternative Android implementation
// e.g.: https://github.com/envoyproxy/envoy-mobile/blob/main/third_party/android/ifaddrs-android.h
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
if (alternate_getifaddrs_.has_value()) {
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.

Shouldn't you be doing this in all cases otherwise the interface docs are misleading?

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.

@mattklein123 I think I might be misunderstanding your point. Where else should I be checking this that is not checked?

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.

You should check this outside of the #ifdef ?

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.

I see what you mean now @mattklein123. My thought was that in posix the class would default to true for supporting getifaddrs regardless of if an alternate implementation is given. Only for Android<24 do we need to check for existence of an alternate implementation.

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.

OK I see. Makes sense. Can you just add a small comment?

/wait

return true;
}
return false;
#else
// Note: posix defaults to true regardless of whether an alternate getifaddrs has been set or not.
// This is because as far as we are aware only Android<24 lacks an implementation and thus another
// posix based platform that lacks a native getifaddrs implementation should be a programming
// error.
//
// That being said, if an alternate getifaddrs impl is set, that will be used in calls to
// OsSysCallsImpl::getifaddrs as seen below.
return true;
#endif
}

SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) {
if (alternate_getifaddrs_.has_value()) {
return alternate_getifaddrs_.value()(interfaces);
}

// TODO: eliminate this branching by upstreaming an alternative Android implementation
// e.g.: https://github.com/envoyproxy/envoy-mobile/blob/main/third_party/android/ifaddrs-android.h
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
#else
struct ifaddrs* ifaddr;
struct ifaddrs* ifa;

const int rc = ::getifaddrs(&ifaddr);
if (rc == -1) {
return {rc, errno};
}

for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) {
continue;
}

if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) {
const sockaddr_storage* ss = reinterpret_cast<sockaddr_storage*>(ifa->ifa_addr);
size_t ss_len =
ifa->ifa_addr->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
StatusOr<Network::Address::InstanceConstSharedPtr> address =
Network::Address::addressFromSockAddr(*ss, ss_len, ifa->ifa_addr->sa_family == AF_INET6);
if (address.ok()) {
interfaces.emplace_back(ifa->ifa_name, ifa->ifa_flags, *address);
}
}
}

if (ifaddr) {
::freeifaddrs(ifaddr);
}

return {rc, 0};
#endif
}

} // namespace Api
} // namespace Envoy
2 changes: 2 additions & 0 deletions source/common/api/posix/os_sys_calls_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class OsSysCallsImpl : public OsSysCalls {
SysCallSocketResult duplicate(os_fd_t oldfd) override;
SysCallSocketResult accept(os_fd_t socket, sockaddr* addr, socklen_t* addrlen) override;
SysCallBoolResult socketTcpInfo(os_fd_t sockfd, EnvoyTcpInfo* tcp_info) override;
bool supportsGetifaddrs() const override;
SysCallIntResult getifaddrs(InterfaceAddressVector& interfaces) override;
};

using OsSysCallsSingleton = ThreadSafeSingleton<OsSysCallsImpl>;
Expand Down
14 changes: 14 additions & 0 deletions source/common/api/win32/os_sys_calls_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -409,5 +409,19 @@ SysCallBoolResult OsSysCallsImpl::socketTcpInfo([[maybe_unused]] os_fd_t sockfd,
return {false, WSAEOPNOTSUPP};
}

bool OsSysCallsImpl::supportsGetifaddrs() const {
if (alternate_getifaddrs_.has_value()) {
return true;
}
return false;
}

SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) {
if (alternate_getifaddrs_.has_value()) {
return alternate_getifaddrs_.value()(interfaces);
}
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
}

} // namespace Api
} // namespace Envoy
2 changes: 2 additions & 0 deletions source/common/api/win32/os_sys_calls_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class OsSysCallsImpl : public OsSysCalls {
SysCallSocketResult duplicate(os_fd_t oldfd) override;
SysCallSocketResult accept(os_fd_t socket, sockaddr* addr, socklen_t* addrlen) override;
SysCallBoolResult socketTcpInfo(os_fd_t sockfd, EnvoyTcpInfo* tcp_info) override;
bool supportsGetifaddrs() const override;
SysCallIntResult getifaddrs(InterfaceAddressVector&) override;
};

using OsSysCallsSingleton = ThreadSafeSingleton<OsSysCallsImpl>;
Expand Down
1 change: 0 additions & 1 deletion source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ envoy_cc_library(
deps = [
":socket_interface_lib",
"//envoy/network:address_interface",
"//source/common/api:os_sys_calls_lib",
"//source/common/common:assert_lib",
"//source/common/common:safe_memcpy_lib",
"//source/common/common:statusor_lib",
Expand Down
32 changes: 9 additions & 23 deletions source/common/network/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -239,36 +239,22 @@ void Utility::throwWithMalformedIp(absl::string_view ip_address) {
// need to be updated in the future. Discussion can be found at Github issue #939.
Address::InstanceConstSharedPtr Utility::getLocalAddress(const Address::IpVersion version) {
Address::InstanceConstSharedPtr ret;
#ifdef SUPPORTS_GETIFADDRS
struct ifaddrs* ifaddr;
struct ifaddrs* ifa;
if (Api::OsSysCallsSingleton::get().supportsGetifaddrs()) {
Api::InterfaceAddressVector interface_addresses{};

const int rc = getifaddrs(&ifaddr);
RELEASE_ASSERT(!rc, "");
const Api::SysCallIntResult rc =
Api::OsSysCallsSingleton::get().getifaddrs(interface_addresses);
RELEASE_ASSERT(!rc.return_value_, fmt::format("getiffaddrs error: {}", rc.errno_));

// man getifaddrs(3)
for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) {
continue;
}

if ((ifa->ifa_addr->sa_family == AF_INET && version == Address::IpVersion::v4) ||
(ifa->ifa_addr->sa_family == AF_INET6 && version == Address::IpVersion::v6)) {
const struct sockaddr_storage* addr =
reinterpret_cast<const struct sockaddr_storage*>(ifa->ifa_addr);
ret = Address::addressFromSockAddrOrThrow(
*addr, (version == Address::IpVersion::v4) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6));
if (!isLoopbackAddress(*ret)) {
// man getifaddrs(3)
for (const auto& interface_address : interface_addresses) {
if (!isLoopbackAddress(*interface_address.interface_addr_)) {
ret = interface_address.interface_addr_;
break;
}
}
}

if (ifaddr) {
freeifaddrs(ifaddr);
}
#endif

// If the local address is not found above, then return the loopback address by default.
if (ret == nullptr) {
if (version == Address::IpVersion::v4) {
Expand Down
17 changes: 17 additions & 0 deletions test/common/api/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_test",
"envoy_package",
)

licenses(["notice"]) # Apache 2

envoy_package()

envoy_cc_test(
name = "os_sys_calls_test",
srcs = ["os_sys_calls_test.cc"],
deps = [
"//source/common/api:os_sys_calls_lib",
],
)
34 changes: 34 additions & 0 deletions test/common/api/os_sys_calls_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "source/common/api/os_sys_calls_impl.h"

#include "gtest/gtest.h"

namespace Envoy {

TEST(OsSyscallsTest, SetAlternateGetifaddrs) {
auto& os_syscalls = Api::OsSysCallsSingleton::get();
const bool pre_alternate_support = os_syscalls.supportsGetifaddrs();
Api::InterfaceAddressVector interfaces{};
#if defined(WIN32) || (defined(__ANDROID_API__) && __ANDROID_API__ < 24)
EXPECT_FALSE(pre_alternate_support);
EXPECT_DEATH(os_syscalls.getifaddrs(interfaces), "not implemented");
#else
EXPECT_TRUE(pre_alternate_support);
const auto pre_alternate_rc = os_syscalls.getifaddrs(interfaces);
EXPECT_EQ(0, pre_alternate_rc.return_value_);
EXPECT_FALSE(interfaces.empty());
#endif

os_syscalls.setAlternateGetifaddrs(
[](Api::InterfaceAddressVector& interfaces) -> Api::SysCallIntResult {
interfaces.emplace_back("made_up_if", 0, nullptr);
return {0, 0};
});
interfaces.clear();

const bool post_alternate_support = os_syscalls.supportsGetifaddrs();
EXPECT_TRUE(post_alternate_support);
EXPECT_EQ(0, os_syscalls.getifaddrs(interfaces).return_value_);
EXPECT_EQ(1, interfaces.size());
EXPECT_EQ("made_up_if", interfaces.front().interface_name_);
}
} // namespace Envoy
16 changes: 16 additions & 0 deletions test/common/http/conn_pool_grid_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -714,8 +714,24 @@ TEST_F(ConnectivityGridTest, ConnectionCloseDuringCreation) {
ASSERT_TRUE(optional_it1.has_value());
EXPECT_EQ("HTTP/3", (**optional_it1)->protocolDescription());

const bool supports_getifaddrs = Api::OsSysCallsSingleton::get().supportsGetifaddrs();
Api::InterfaceAddressVector interfaces{};
if (supports_getifaddrs) {
ASSERT_EQ(0, Api::OsSysCallsSingleton::get().getifaddrs(interfaces).return_value_);
}

Api::MockOsSysCalls os_sys_calls;
TestThreadsafeSingletonInjector<Api::OsSysCallsImpl> os_calls(&os_sys_calls);
EXPECT_CALL(os_sys_calls, supportsGetifaddrs()).WillOnce(Return(supports_getifaddrs));
if (supports_getifaddrs) {
EXPECT_CALL(os_sys_calls, getifaddrs(_))
.WillOnce(
Invoke([&](Api::InterfaceAddressVector& interface_vector) -> Api::SysCallIntResult {
interface_vector.insert(interface_vector.begin(), interfaces.begin(),
interfaces.end());
return {0, 0};
}));
}
EXPECT_CALL(os_sys_calls, socket(_, _, _)).WillOnce(Return(Api::SysCallSocketResult{1, 0}));
#if defined(__APPLE__) || defined(WIN32)
EXPECT_CALL(os_sys_calls, setsocketblocking(1, false))
Expand Down
3 changes: 3 additions & 0 deletions test/mocks/api/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ class MockOsSysCalls : public OsSysCallsImpl {
MOCK_METHOD(bool, supportsUdpGro, (), (const));
MOCK_METHOD(bool, supportsIpTransparent, (), (const));
MOCK_METHOD(bool, supportsMptcp, (), (const));
MOCK_METHOD(bool, supportsGetifaddrs, (), (const));
MOCK_METHOD(void, setAlternateGetifaddrs, (AlternateGetifaddrs alternate_getifaddrs));
MOCK_METHOD(SysCallIntResult, getifaddrs, (InterfaceAddressVector & interfaces));

// Map from (sockfd,level,optname) to boolean socket option.
using SockOptKey = std::tuple<os_fd_t, int, int>;
Expand Down
Loading