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

Update network utility getoriginaldst for IPv6 #3933

Merged
merged 3 commits into from
Jul 24, 2018
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
1 change: 1 addition & 0 deletions source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ envoy_cc_library(
":address_lib",
"//include/envoy/network:connection_interface",
"//include/envoy/stats:stats_interface",
"//source/common/api:os_sys_calls_lib",
"//source/common/common:assert_lib",
"//source/common/common:utility_lib",
"//source/common/protobuf",
Expand Down
37 changes: 32 additions & 5 deletions source/common/network/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
#include <linux/netfilter_ipv4.h>
#endif

#ifndef IP6T_SO_ORIGINAL_DST
// From linux/netfilter_ipv6/ip6_tables.h
#define IP6T_SO_ORIGINAL_DST 80
#endif

#include <netinet/ip.h>
#include <sys/socket.h>

Expand All @@ -20,6 +25,7 @@
#include "envoy/network/connection.h"
#include "envoy/stats/stats.h"

#include "common/api/os_sys_calls_impl.h"
#include "common/common/assert.h"
#include "common/common/utility.h"
#include "common/network/address_impl.h"
Expand Down Expand Up @@ -292,14 +298,35 @@ Address::InstanceConstSharedPtr Utility::getOriginalDst(int fd) {
#ifdef SOL_IP
sockaddr_storage orig_addr;
socklen_t addr_len = sizeof(sockaddr_storage);
int status = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, &orig_addr, &addr_len);
int socket_domain;
socklen_t domain_len = sizeof(socket_domain);
auto& os_syscalls = Api::OsSysCallsSingleton::get();
int status = os_syscalls.getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &socket_domain, &domain_len);

if (status != 0) {
return nullptr;
}

if (status == 0) {
// TODO(mattklein123): IPv6 support. See github issue #1094.
ASSERT(orig_addr.ss_family == AF_INET);
if (socket_domain == AF_INET) {
status = os_syscalls.getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, &orig_addr, &addr_len);
} else if (socket_domain == AF_INET6) {
status = os_syscalls.getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, &orig_addr, &addr_len);
} else {
return nullptr;
}

if (status != 0) {
return nullptr;
}

switch (orig_addr.ss_family) {
case AF_INET:
return Address::InstanceConstSharedPtr{
new Address::Ipv4Instance(reinterpret_cast<sockaddr_in*>(&orig_addr))};
} else {
case AF_INET6:
return Address::InstanceConstSharedPtr{
new Address::Ipv6Instance(reinterpret_cast<sockaddr_in6&>(orig_addr))};
default:
return nullptr;
}
#else
Expand Down
137 changes: 137 additions & 0 deletions test/server/listener_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2269,6 +2269,143 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterOptionFail)
EXPECT_EQ(0U, manager_->listeners().size());
}

class OriginalDstTestFilterIPv6
: public Extensions::ListenerFilters::OriginalDst::OriginalDstFilter {
Network::Address::InstanceConstSharedPtr getOriginalDst(int) override {
return Network::Address::InstanceConstSharedPtr{
new Network::Address::Ipv6Instance("1::2", 2345)};
}
};

TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterIPv6) {
static int fd;
fd = -1;
EXPECT_CALL(*listener_factory_.socket_, fd()).WillOnce(Return(0));

class OriginalDstTestConfigFactory : public Configuration::NamedListenerFilterConfigFactory {
public:
// NamedListenerFilterConfigFactory
Network::ListenerFilterFactoryCb
createFilterFactoryFromProto(const Protobuf::Message&,
Configuration::ListenerFactoryContext& context) override {
auto option = std::make_unique<Network::MockSocketOption>();
EXPECT_CALL(*option, setOption(_, envoy::api::v2::core::SocketOption::STATE_PREBIND))
.WillOnce(Return(true));
EXPECT_CALL(*option, setOption(_, envoy::api::v2::core::SocketOption::STATE_BOUND))
.WillOnce(Invoke(
[](Network::Socket& socket, envoy::api::v2::core::SocketOption::SocketState) -> bool {
fd = socket.fd();
return true;
}));
context.addListenSocketOption(std::move(option));
return [](Network::ListenerFilterManager& filter_manager) -> void {
filter_manager.addAcceptFilter(std::make_unique<OriginalDstTestFilterIPv6>());
};
}

ProtobufTypes::MessagePtr createEmptyConfigProto() override {
return std::make_unique<Envoy::ProtobufWkt::Empty>();
}

std::string name() override { return "test.listener.original_dstipv6"; }
};

/**
* Static registration for the original dst filter. @see RegisterFactory.
*/
static Registry::RegisterFactory<OriginalDstTestConfigFactory,
Configuration::NamedListenerFilterConfigFactory>
registered_;

const std::string yaml = TestEnvironment::substitute(R"EOF(
address:
socket_address: { address: ::0001, port_value: 1111 }
filter_chains: {}
listener_filters:
- name: "test.listener.original_dstipv6"
config: {}
)EOF",
Network::Address::IpVersion::v6);

EXPECT_CALL(server_.random_, uuid());
EXPECT_CALL(listener_factory_, createListenSocket(_, _, true));
manager_->addOrUpdateListener(parseListenerFromV2Yaml(yaml), "", true);
EXPECT_EQ(1U, manager_->listeners().size());

Network::ListenerConfig& listener = manager_->listeners().back().get();

Network::FilterChainFactory& filterChainFactory = listener.filterChainFactory();
Network::MockListenerFilterManager manager;

NiceMock<Network::MockListenerFilterCallbacks> callbacks;
Network::AcceptedSocketImpl socket(
-1, std::make_unique<Network::Address::Ipv6Instance>("::0001", 1234),
std::make_unique<Network::Address::Ipv6Instance>("::0001", 5678));

EXPECT_CALL(callbacks, socket()).WillOnce(Invoke([&]() -> Network::ConnectionSocket& {
return socket;
}));

EXPECT_CALL(manager, addAcceptFilter_(_))
.WillOnce(Invoke([&](Network::ListenerFilterPtr& filter) -> void {
EXPECT_EQ(Network::FilterStatus::Continue, filter->onAccept(callbacks));
}));

EXPECT_TRUE(filterChainFactory.createListenerFilterChain(manager));
EXPECT_TRUE(socket.localAddressRestored());
EXPECT_EQ("[1::2]:2345", socket.localAddress()->asString());
EXPECT_NE(fd, -1);
}

TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterOptionFailIPv6) {
class OriginalDstTestConfigFactory : public Configuration::NamedListenerFilterConfigFactory {
public:
// NamedListenerFilterConfigFactory
Network::ListenerFilterFactoryCb
createFilterFactoryFromProto(const Protobuf::Message&,
Configuration::ListenerFactoryContext& context) override {
auto option = std::make_unique<Network::MockSocketOption>();
EXPECT_CALL(*option, setOption(_, envoy::api::v2::core::SocketOption::STATE_PREBIND))
.WillOnce(Return(false));
context.addListenSocketOption(std::move(option));
return [](Network::ListenerFilterManager& filter_manager) -> void {
filter_manager.addAcceptFilter(std::make_unique<OriginalDstTestFilterIPv6>());
};
}

ProtobufTypes::MessagePtr createEmptyConfigProto() override {
return std::make_unique<Envoy::ProtobufWkt::Empty>();
}

std::string name() override { return "testfail.listener.original_dstipv6"; }
};

/**
* Static registration for the original dst filter. @see RegisterFactory.
*/
static Registry::RegisterFactory<OriginalDstTestConfigFactory,
Configuration::NamedListenerFilterConfigFactory>
registered_;

const std::string yaml = TestEnvironment::substitute(R"EOF(
name: "socketOptionFailListener"
address:
socket_address: { address: ::0001, port_value: 1111 }
filter_chains: {}
listener_filters:
- name: "testfail.listener.original_dstipv6"
config: {}
)EOF",
Network::Address::IpVersion::v6);

EXPECT_CALL(listener_factory_, createListenSocket(_, _, true));

EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(parseListenerFromV2Yaml(yaml), "", true),
EnvoyException,
"MockListenerComponentFactory: Setting socket options failed");
EXPECT_EQ(0U, manager_->listeners().size());
}

// Validate that when neither transparent nor freebind is not set in the
// Listener, we see no socket option set.
TEST_F(ListenerManagerImplWithRealFiltersTest, TransparentFreebindListenerDisabled) {
Expand Down