blob: e1f5cdf59f91c178f0db635b1dc24af2718cd91e [file] [log] [blame]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "dns-proxy/proxy.h"
#include <fcntl.h>
#include <linux/rtnetlink.h>
#include <sys/stat.h>
#include <memory>
#include <optional>
#include <string_view>
#include <utility>
#include <vector>
#include <base/functional/callback.h>
#include <chromeos/net-base/byte_utils.h>
#include <chromeos/net-base/ip_address.h>
#include <chromeos/net-base/rtnl_message.h>
#include <chromeos/patchpanel/address_manager.h>
#include <chromeos/patchpanel/dbus/fake_client.h>
#include <chromeos/patchpanel/mock_message_dispatcher.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_object_proxy.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <shill/dbus-constants.h>
#include <shill/dbus-proxy-mocks.h>
#include <shill/dbus/client/fake_client.h>
#include "dns-proxy/ipc.pb.h"
namespace dns_proxy {
namespace {
constexpr net_base::IPv4Address kNetnsPeerIPv4Aaddr(100, 115, 92, 130);
constexpr net_base::IPv6Address kNetnsPeerIPv6Aaddr(
0xfd, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01);
constexpr base::TimeDelta kRequestTimeout = base::Seconds(10000);
constexpr base::TimeDelta kRequestRetryDelay = base::Milliseconds(200);
constexpr int32_t kRequestMaxRetry = 1;
int make_fd() {
return open("/dev/null", O_RDONLY);
}
// A helper function to convert a list of IP addresses from type std::string to
// net_base::IPAddress. If |list2| is provided, its elements will be appended to
// the results. This function assumes that all input strings are valid IP
// addresses, otherwise it will crash directly.
std::vector<net_base::IPAddress> StringsToIPAddressesChecked(
const std::vector<std::string>& list1,
const std::vector<std::string>& list2 = {}) {
std::vector<net_base::IPAddress> ret;
for (const auto& str : list1) {
ret.push_back(*net_base::IPAddress::CreateFromString(str));
}
for (const auto& str : list2) {
ret.push_back(*net_base::IPAddress::CreateFromString(str));
}
return ret;
}
} // namespace
using org::chromium::flimflam::ManagerProxyInterface;
using org::chromium::flimflam::ManagerProxyMock;
using testing::_;
using testing::ByMove;
using testing::DoAll;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::Return;
using testing::SetArgPointee;
using testing::StrEq;
MATCHER_P(EqualsProto,
message,
"Match a proto Message equal to the matcher's argument.") {
std::string expected_serialized, actual_serialized;
message.SerializeToString(&expected_serialized);
arg.SerializeToString(&actual_serialized);
return expected_serialized == actual_serialized;
}
patchpanel::Client::VirtualDevice virtualdev(
patchpanel::Client::GuestType guest_type,
const std::string& ifname,
const std::string& phys_ifname,
const net_base::IPv4Address& host_ipv4_addr = {}) {
patchpanel::Client::VirtualDevice device;
device.ifname = ifname;
device.phys_ifname = phys_ifname;
device.guest_type = guest_type;
device.host_ipv4_addr = host_ipv4_addr;
return device;
}
class FakeShillClient : public shill::FakeClient {
public:
FakeShillClient(scoped_refptr<dbus::Bus> bus,
ManagerProxyInterface* manager_proxy)
: shill::FakeClient(bus), manager_proxy_(manager_proxy) {}
std::unique_ptr<shill::Client::ManagerPropertyAccessor> ManagerProperties(
const base::TimeDelta& timeout) const override {
return std::make_unique<shill::Client::ManagerPropertyAccessor>(
manager_proxy_);
}
std::unique_ptr<shill::Client::Device> DefaultDevice(
bool exclude_vpn) override {
return std::move(default_device_);
}
ManagerProxyInterface* GetManagerProxy() const override {
return manager_proxy_;
}
std::unique_ptr<shill::Client::Device> default_device_;
private:
ManagerProxyInterface* manager_proxy_;
};
class MockPatchpanelClient : public patchpanel::FakeClient {
public:
MockPatchpanelClient() = default;
~MockPatchpanelClient() override = default;
MOCK_METHOD(
(std::pair<base::ScopedFD, patchpanel::Client::ConnectedNamespace>),
ConnectNamespace,
(pid_t pid,
const std::string& outbound_ifname,
bool forward_user_traffic,
bool route_on_vpn,
patchpanel::Client::TrafficSource traffic_source,
bool static_ipv6),
(override));
MOCK_METHOD(base::ScopedFD,
RedirectDns,
(patchpanel::Client::DnsRedirectionRequestType,
const std::string&,
const std::string&,
const std::vector<std::string>&,
const std::string&),
(override));
MOCK_METHOD(std::vector<patchpanel::Client::VirtualDevice>,
GetDevices,
(),
(override));
};
class MockResolver : public Resolver {
public:
MockResolver()
: Resolver(base::DoNothing(),
kRequestTimeout,
kRequestRetryDelay,
kRequestMaxRetry) {}
~MockResolver() override = default;
MOCK_METHOD(bool,
ListenUDP,
(struct sockaddr*, std::string_view),
(override));
MOCK_METHOD(bool,
ListenTCP,
(struct sockaddr*, std::string_view),
(override));
MOCK_METHOD(void, StopListen, (sa_family_t, std::string_view), (override));
MOCK_METHOD(void,
SetNameServers,
(const std::vector<std::string>&),
(override));
MOCK_METHOD(void,
SetDoHProviders,
(const std::vector<std::string>&, bool),
(override));
MOCK_METHOD(void, SetInterface, (std::string_view), (override));
MOCK_METHOD(void, ClearInterface, (), (override));
};
class TestProxy : public Proxy {
public:
TestProxy(const Options& opts,
std::unique_ptr<patchpanel::Client> patchpanel,
std::unique_ptr<shill::Client> shill,
std::unique_ptr<patchpanel::MessageDispatcher<SubprocessMessage>>
msg_dispatcher,
bool root_ns_enabled_)
: Proxy(opts,
std::move(patchpanel),
std::move(shill),
std::move(msg_dispatcher),
root_ns_enabled_) {}
std::unique_ptr<Resolver> resolver;
std::unique_ptr<Resolver> NewResolver(base::TimeDelta timeout,
base::TimeDelta retry_delay,
int max_num_retries) override {
return std::move(resolver);
}
std::map<std::string, int> ifindexes;
// Returns a consistent mapping between |ifname| and its index value.
int IfNameToIndex(const char* ifname) override {
static int cur_index = 1;
const std::string ifname_str(ifname);
const auto it = ifindexes.find(ifname_str);
if (it != ifindexes.end()) {
return it->second;
}
ifindexes[ifname_str] = cur_index;
return cur_index++;
}
};
class ProxyTest : public ::testing::Test,
public ::testing::WithParamInterface<bool> {
protected:
ProxyTest()
: mock_bus_(new dbus::MockBus{dbus::Bus::Options{}}),
mock_proxy_(new dbus::MockObjectProxy(mock_bus_.get(),
shill::kFlimflamServiceName,
dbus::ObjectPath("/"))) {}
~ProxyTest() override { mock_bus_->ShutdownAndBlock(); }
void SetUp() override {
EXPECT_CALL(*mock_bus_, GetObjectProxy(_, _))
.WillRepeatedly(Return(mock_proxy_.get()));
}
void SetUpProxy(bool root_ns_enabled,
const Proxy::Options& opts,
std::unique_ptr<shill::Client::Device> device = nullptr,
bool set_resolver = true) {
// Set up mocks and fakes.
patchpanel_client_ = new MockPatchpanelClient();
shill_client_ = new FakeShillClient(
mock_bus_, reinterpret_cast<ManagerProxyInterface*>(
const_cast<ManagerProxyMock*>(&mock_manager_)));
msg_dispatcher_ =
new patchpanel::MockMessageDispatcher<SubprocessMessage>();
// Initialize Proxy instance.
proxy_ = std::make_unique<TestProxy>(
opts, base::WrapUnique(patchpanel_client_),
base::WrapUnique(shill_client_), base::WrapUnique(msg_dispatcher_),
root_ns_enabled);
// Initialize default proxy behaviors.
proxy_->shill_ready_ = true;
proxy_->device_ = std::move(device);
if (set_resolver) {
resolver_ = new MockResolver();
proxy_->resolver_ = base::WrapUnique(resolver_);
proxy_->doh_config_.set_resolver(resolver_);
}
// Initialize default mocks behavior.
if (opts.type == Proxy::Type::kSystem) {
ON_CALL(mock_manager_, SetDNSProxyAddresses(_, _, _))
.WillByDefault(Return(true));
ON_CALL(*msg_dispatcher_, SendMessage(_)).WillByDefault(Return(true));
}
// Initialize expected addresses.
if (root_ns_enabled) {
switch (opts.type) {
case Proxy::Type::kSystem:
ipv4_address_ = patchpanel::kDnsProxySystemIPv4Address;
ipv6_address_ = patchpanel::kDnsProxySystemIPv6Address;
break;
case Proxy::Type::kDefault:
ipv4_address_ = patchpanel::kDnsProxyDefaultIPv4Address;
ipv6_address_ = patchpanel::kDnsProxyDefaultIPv6Address;
break;
default:
break;
}
} else {
ipv4_address_ = kNetnsPeerIPv4Aaddr;
ipv6_address_ = kNetnsPeerIPv6Aaddr;
}
}
std::unique_ptr<shill::Client::Device> ShillDevice(
shill::Client::Device::ConnectionState state =
shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type type = shill::Client::Device::Type::kEthernet,
const std::string& ifname = "",
std::vector<std::string> ipv4_nameservers = {"8.8.8.8"},
std::vector<std::string> ipv6_nameservers = {"2001:4860:4860::8888"}) {
auto dev = std::make_unique<shill::Client::Device>();
dev->type = type;
dev->state = state;
dev->ifname = ifname;
dev->network_config.dns_servers =
StringsToIPAddressesChecked(ipv6_nameservers, ipv4_nameservers);
return dev;
}
void SetListenAddresses(
const std::optional<net_base::IPv4Address>& ipv4_addr,
const std::optional<net_base::IPv6Address>& ipv6_addr) {
proxy_->initialized_ = true;
proxy_->ipv4_address_ = ipv4_addr;
proxy_->ipv6_address_ = ipv6_addr;
if (proxy_->root_ns_enabled_) {
return;
}
proxy_->ns_fd_ = base::ScopedFD(make_fd());
if (ipv4_addr) {
proxy_->ns_.peer_ipv4_address = *ipv4_addr;
}
}
void SetNameServers(const std::vector<std::string>& ipv4_nameservers,
const std::vector<std::string>& ipv6_nameservers) {
EXPECT_TRUE(proxy_->device_);
proxy_->device_->network_config.dns_servers =
StringsToIPAddressesChecked(ipv6_nameservers, ipv4_nameservers);
proxy_->UpdateNameServers();
}
void SetInterfaceIPv6Address(const std::string& ifname,
const net_base::IPv6Address& addr) {
int ifindex = proxy_->IfNameToIndex(ifname.c_str());
proxy_->link_local_addresses_[ifindex] = addr;
}
protected:
scoped_refptr<dbus::MockBus> mock_bus_;
scoped_refptr<dbus::MockObjectProxy> mock_proxy_;
ManagerProxyMock mock_manager_;
MockResolver* resolver_;
patchpanel::MockMessageDispatcher<SubprocessMessage>* msg_dispatcher_;
FakeShillClient* shill_client_;
MockPatchpanelClient* patchpanel_client_;
std::unique_ptr<TestProxy> proxy_;
net_base::IPv4Address ipv4_address_;
net_base::IPv6Address ipv6_address_;
};
// Test with DNS proxy running on root namespace and inside a network namespace.
INSTANTIATE_TEST_SUITE_P(ProxyTestInstance,
ProxyTest,
::testing::Values(false, true));
TEST_P(ProxyTest, NonSystemProxy_OnShutdownDoesNotCallShill) {
EXPECT_CALL(mock_manager_, SetDNSProxyAddresses(_, _, _)).Times(0);
EXPECT_CALL(mock_manager_, ClearDNSProxyAddresses(_, _)).Times(0);
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice());
int unused;
proxy_->OnShutdown(&unused);
}
TEST_P(ProxyTest, SystemProxy_SendIPAddressesToController) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice());
SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"});
ProxyMessage proxy_msg;
proxy_msg.set_type(ProxyMessage::SET_ADDRS);
proxy_msg.add_addrs(ipv4_address_.ToString());
proxy_msg.add_addrs(ipv6_address_.ToString());
SubprocessMessage msg;
*msg.mutable_proxy_message() = proxy_msg;
EXPECT_CALL(*msg_dispatcher_, SendMessage(EqualsProto(msg)))
.WillOnce(Return(true));
proxy_->SendIPAddressesToController(ipv4_address_, ipv6_address_);
}
TEST_P(ProxyTest, SystemProxy_SendIPAddressesToControllerEmptyNameserver) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice());
// Only IPv4 nameserver.
SetNameServers({"8.8.8.8"}, /*ipv6_nameservers=*/{});
ProxyMessage proxy_msg;
proxy_msg.set_type(ProxyMessage::SET_ADDRS);
proxy_msg.add_addrs(ipv4_address_.ToString());
SubprocessMessage msg;
*msg.mutable_proxy_message() = proxy_msg;
EXPECT_CALL(*msg_dispatcher_, SendMessage(EqualsProto(msg)))
.WillOnce(Return(true));
proxy_->SendIPAddressesToController(ipv4_address_, ipv6_address_);
// Only IPv6 nameserver.
SetNameServers(/*ipv4_nameservers=*/{}, {"2001:4860:4860::8888"});
proxy_msg.Clear();
proxy_msg.set_type(ProxyMessage::SET_ADDRS);
proxy_msg.add_addrs(ipv6_address_.ToString());
*msg.mutable_proxy_message() = proxy_msg;
EXPECT_CALL(*msg_dispatcher_, SendMessage(EqualsProto(msg)))
.WillOnce(Return(true));
proxy_->SendIPAddressesToController(ipv4_address_, ipv6_address_);
}
TEST_P(ProxyTest, SystemProxy_ClearIPAddressesInController) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
EXPECT_CALL(*msg_dispatcher_, SendMessage(_)).WillOnce(Return(true));
proxy_->ClearIPAddressesInController();
}
TEST_P(ProxyTest, ShillInitializedWhenReady) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
// Test class defaults to make shill client ready. Reset to false.
proxy_->shill_ready_ = false;
proxy_->OnShillReady(true);
EXPECT_TRUE(proxy_->shill_ready_);
}
TEST_P(ProxyTest, SystemProxy_ConnectedNamedspace) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*patchpanel_client_, ConnectNamespace(_, _, _, _, _, _))
.Times(0);
} else {
EXPECT_CALL(
*patchpanel_client_,
ConnectNamespace(_, _, /*outbound_ifname=*/"", /*route_on_vpn=*/false,
patchpanel::Client::TrafficSource::kSystem, _))
.WillOnce(
Return(std::make_pair(base::ScopedFD(make_fd()),
patchpanel::Client::ConnectedNamespace{})));
}
proxy_->OnPatchpanelReady(true);
}
TEST_P(ProxyTest, DefaultProxy_ConnectedNamedspace) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice());
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*patchpanel_client_, ConnectNamespace(_, _, _, _, _, _))
.Times(0);
} else {
EXPECT_CALL(
*patchpanel_client_,
ConnectNamespace(_, _, /*outbound_ifname=*/"", /*route_on_vpn=*/true,
patchpanel::Client::TrafficSource::kUser, _))
.WillOnce(
Return(std::make_pair(base::ScopedFD(make_fd()),
patchpanel::Client::ConnectedNamespace{})));
}
proxy_->OnPatchpanelReady(true);
}
TEST_P(ProxyTest, ArcProxy_ConnectedNamedspace) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"});
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*patchpanel_client_, ConnectNamespace(_, _, _, _, _, _))
.Times(0);
} else {
EXPECT_CALL(*patchpanel_client_,
ConnectNamespace(_, _, /*outbound_ifname=*/"eth0",
/*route_on_vpn=*/false,
patchpanel::Client::TrafficSource::kArc, _))
.WillOnce(
Return(std::make_pair(base::ScopedFD(make_fd()),
patchpanel::Client::ConnectedNamespace{})));
}
proxy_->OnPatchpanelReady(true);
}
TEST_P(ProxyTest, StateClearedIfDefaultServiceDrops) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice());
proxy_->OnDefaultDeviceChanged(nullptr /* no service */);
EXPECT_FALSE(proxy_->device_);
EXPECT_FALSE(proxy_->resolver_);
}
TEST_P(ProxyTest, ArcProxy_IgnoredIfDefaultServiceDrops) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice());
proxy_->OnDefaultDeviceChanged(nullptr /* no service */);
EXPECT_TRUE(proxy_->device_);
EXPECT_TRUE(proxy_->resolver_);
}
TEST_P(ProxyTest, StateClearedIfDefaultServiceIsNotOnline) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice());
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kReady);
proxy_->OnDefaultDeviceChanged(dev.get());
EXPECT_FALSE(proxy_->device_);
EXPECT_FALSE(proxy_->resolver_);
}
TEST_P(ProxyTest, NewResolverStartsListeningOnDefaultServiceComesOnline) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
/*device=*/nullptr, /*set_resolver=*/false);
SetListenAddresses(ipv4_address_, ipv6_address_);
auto* new_resolver = new MockResolver();
proxy_->resolver = base::WrapUnique(new_resolver);
if (proxy_->root_ns_enabled_) {
// Called for both IPv4 and IPv6.
EXPECT_CALL(*new_resolver, ListenUDP(_, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(*new_resolver, ListenTCP(_, _))
.Times(2)
.WillRepeatedly(Return(true));
} else {
// Called for IPv6 only.
EXPECT_CALL(*new_resolver, ListenUDP(_, _)).WillOnce(Return(true));
EXPECT_CALL(*new_resolver, ListenTCP(_, _)).WillOnce(Return(true));
}
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline);
brillo::VariantDictionary props;
EXPECT_CALL(mock_manager_, GetProperties(_, _, _))
.WillOnce(DoAll(SetArgPointee<0>(props), Return(true)));
proxy_->OnDefaultDeviceChanged(dev.get());
EXPECT_TRUE(proxy_->resolver_);
}
TEST_P(ProxyTest, NameServersUpdatedOnDefaultServiceComesOnline) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault});
SetListenAddresses(ipv4_address_, ipv6_address_);
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0",
{"8.8.8.8", "8.8.4.4"},
{"2001:4860:4860::8888", "2001:4860:4860::8844"});
EXPECT_CALL(*resolver_,
SetNameServers(ElementsAre(StrEq("8.8.8.8"), StrEq("8.8.4.4"),
StrEq("2001:4860:4860::8888"),
StrEq("2001:4860:4860::8844"))));
proxy_->OnDefaultDeviceChanged(dev.get());
}
TEST_P(ProxyTest, SystemProxy_IgnoresVPN) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
SetListenAddresses(ipv4_address_, ipv6_address_);
// Expect default device changes to WiFi.
auto wifi = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kWifi);
proxy_->OnDefaultDeviceChanged(wifi.get());
EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi);
// Expect default device to still be WiFi even when a VPN is active.
auto vpn = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kVPN);
proxy_->OnDefaultDeviceChanged(vpn.get());
EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi);
}
TEST_P(ProxyTest, SystemProxy_GetsPhysicalDeviceOnInitialVPN) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
SetListenAddresses(ipv4_address_, ipv6_address_);
shill_client_->default_device_ =
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kWifi);
auto vpn = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kVPN);
proxy_->OnDefaultDeviceChanged(vpn.get());
EXPECT_TRUE(proxy_->device_);
EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi);
}
TEST_P(ProxyTest, DefaultProxy_UsesVPN) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault});
SetListenAddresses(ipv4_address_, ipv6_address_);
auto wifi = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kWifi);
proxy_->OnDefaultDeviceChanged(wifi.get());
EXPECT_TRUE(proxy_->device_);
EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kWifi);
auto vpn = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kVPN);
proxy_->OnDefaultDeviceChanged(vpn.get());
EXPECT_TRUE(proxy_->device_);
EXPECT_EQ(proxy_->device_->type, shill::Client::Device::Type::kVPN);
}
TEST_P(ProxyTest, ArcProxy_NameServersUpdatedOnDeviceChangeEvent) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"});
SetListenAddresses(ipv4_address_, ipv6_address_);
// Set name servers on device change event.
auto wifi = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kWifi, "wlan0",
{"8.8.8.8"}, {"2001:4860:4860::8888"});
EXPECT_CALL(*resolver_,
SetNameServers(ElementsAre(StrEq("8.8.8.8"),
StrEq("2001:4860:4860::8888"))));
proxy_->OnDeviceChanged(wifi.get());
// Verify it only applies changes for the correct interface.
auto eth = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0",
{"8.8.8.8", "8.8.4.4"},
{"2001:4860:4860::8888", "2001:4860:4860::8844"});
EXPECT_CALL(*resolver_, SetNameServers(_)).Times(0);
proxy_->OnDeviceChanged(eth.get());
// Update WiFi device nameservers.
wifi->network_config.dns_servers = StringsToIPAddressesChecked(
{"2001:4860:4860::8888", "2001:4860:4860::8844", "8.8.8.8", "8.8.4.4"});
EXPECT_CALL(*resolver_,
SetNameServers(ElementsAre(StrEq("8.8.8.8"), StrEq("8.8.4.4"),
StrEq("2001:4860:4860::8888"),
StrEq("2001:4860:4860::8844"))));
proxy_->OnDeviceChanged(wifi.get());
}
TEST_P(ProxyTest, SystemProxy_NameServersUpdatedOnDeviceChangeEvent) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
SetListenAddresses(ipv4_address_, ipv6_address_);
// Set name servers on device change event.
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0",
{"8.8.8.8"}, {"2001:4860:4860::8888"});
EXPECT_CALL(*resolver_,
SetNameServers(ElementsAre(StrEq("8.8.8.8"),
StrEq("2001:4860:4860::8888"))));
proxy_->OnDefaultDeviceChanged(dev.get());
// Now trigger an NetworkConfig change.
dev->network_config.dns_servers = StringsToIPAddressesChecked(
{"2001:4860:4860::8888", "2001:4860:4860::8844", "8.8.8.8", "8.8.4.4"});
EXPECT_CALL(*resolver_,
SetNameServers(ElementsAre(StrEq("8.8.8.8"), StrEq("8.8.4.4"),
StrEq("2001:4860:4860::8888"),
StrEq("2001:4860:4860::8844"))));
proxy_->OnDeviceChanged(dev.get());
}
TEST_P(ProxyTest, DeviceChangeEventIgnored) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
SetListenAddresses(ipv4_address_, ipv6_address_);
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0");
// Set name servers on device change event.
EXPECT_CALL(*resolver_, SetNameServers(_)).Times(1);
proxy_->OnDefaultDeviceChanged(dev.get());
// No change to NetworkConfig, no call to SetNameServers
EXPECT_CALL(*resolver_, SetNameServers(_)).Times(0);
proxy_->OnDeviceChanged(dev.get());
// Different ifname, no call to SetNameServers
EXPECT_CALL(*resolver_, SetNameServers(_)).Times(0);
dev->ifname = "eth1";
proxy_->OnDeviceChanged(dev.get());
}
TEST_P(ProxyTest, BasicDoHDisable) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false));
brillo::VariantDictionary props;
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, BasicDoHAlwaysOn) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
EXPECT_CALL(
*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), true));
brillo::VariantDictionary props;
props["https://dns.google.com"] = std::string("");
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, BasicDoHAutomatic) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(
*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), false));
brillo::VariantDictionary props;
props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4");
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, BasicDoHSecureWithFallback) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://custom-provider.com")),
false));
brillo::VariantDictionary props;
props["https://custom-provider.com"] =
std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress);
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, RemovesDNSQueryParameterTemplate_AlwaysOn) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
EXPECT_CALL(
*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), true));
brillo::VariantDictionary props;
props["https://dns.google.com{?dns}"] = std::string("");
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, RemovesDNSQueryParameterTemplate_Automatic) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(
*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), false));
brillo::VariantDictionary props;
props["https://dns.google.com{?dns}"] = std::string("8.8.8.8, 8.8.4.4");
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, RemovesDNSQueryParameterTemplate_SecureWithFallback) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
SetNameServers({"8.8.4.4"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://custom-provider.com")),
false));
brillo::VariantDictionary props;
props["https://custom-provider.com{?dns}"] =
std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress);
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, NewResolverConfiguredWhenSet) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
brillo::VariantDictionary props;
props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4");
props["https://chrome.cloudflare-dns.com/dns-query"] =
std::string("1.1.1.1,2606:4700:4700::1111");
proxy_->OnDoHProvidersChanged(props);
SetNameServers({"1.0.0.1", "1.1.1.1"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(*resolver_, SetNameServers(UnorderedElementsAre(
StrEq("1.1.1.1"), StrEq("1.0.0.1"))));
EXPECT_CALL(
*resolver_,
SetDoHProviders(
ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")),
false));
proxy_->doh_config_.set_resolver(resolver_);
}
TEST_P(ProxyTest, DoHModeChangingFixedNameServers) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
// Initially off.
EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false));
SetNameServers({"1.1.1.1", "9.9.9.9"}, /*ipv6_nameservers=*/{});
// Automatic mode - matched cloudflare.
EXPECT_CALL(
*resolver_,
SetDoHProviders(
ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")),
false));
brillo::VariantDictionary props;
props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4");
props["https://chrome.cloudflare-dns.com/dns-query"] =
std::string("1.1.1.1,2606:4700:4700::1111");
proxy_->OnDoHProvidersChanged(props);
// Automatic mode - no match.
EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false));
SetNameServers({"10.10.10.1"}, /*ipv6_nameservers=*/{});
// Automatic mode - matched google.
EXPECT_CALL(
*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://dns.google.com")), false));
SetNameServers({"8.8.4.4", "10.10.10.1", "8.8.8.8"}, /*ipv6_nameservers=*/{});
// Automatic mode - secure DNS with fallback.
EXPECT_CALL(
*resolver_,
SetDoHProviders(
ElementsAre(StrEq("https://custom-provider.com/dns-query")), false));
props["https://custom-provider.com/dns-query"] =
std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress);
proxy_->OnDoHProvidersChanged(props);
// Explicitly turned off.
EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false));
props.clear();
proxy_->OnDoHProvidersChanged(props);
// Still off - even switching ns back.
EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false));
SetNameServers({"8.8.4.4", "10.10.10.1", "8.8.8.8"}, /*ipv6_nameservers=*/{});
// Always-on mode.
EXPECT_CALL(
*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://doh.opendns.com/dns-query")),
true));
props.clear();
props["https://doh.opendns.com/dns-query"] = std::string("");
proxy_->OnDoHProvidersChanged(props);
// Back to automatic mode, though no matching ns.
EXPECT_CALL(*resolver_, SetDoHProviders(IsEmpty(), false));
props.clear();
props["https://doh.opendns.com/dns-query"] = std::string(
"208.67.222.222,208.67.220.220,2620:119:35::35, 2620:119:53::53");
proxy_->OnDoHProvidersChanged(props);
// Automatic mode working on ns update.
EXPECT_CALL(
*resolver_,
SetDoHProviders(ElementsAre(StrEq("https://doh.opendns.com/dns-query")),
false));
SetNameServers({"8.8.8.8"}, {"2620:119:35::35"});
}
TEST_P(ProxyTest, MultipleDoHProvidersForAlwaysOnMode) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
EXPECT_CALL(*resolver_, SetDoHProviders(UnorderedElementsAre(
StrEq("https://dns.google.com"),
StrEq("https://doh.opendns.com")),
true));
brillo::VariantDictionary props;
props["https://dns.google.com"] = std::string("");
props["https://doh.opendns.com"] = std::string("");
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, MultipleDoHProvidersForAutomaticMode) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
SetNameServers({"1.1.1.1", "10.10.10.10"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(
*resolver_,
SetDoHProviders(
ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")),
false));
brillo::VariantDictionary props;
props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4");
props["https://dns.quad9.net/dns-query"] = std::string("9.9.9.9,2620:fe::9");
props["https://chrome.cloudflare-dns.com/dns-query"] =
std::string("1.1.1.1,2606:4700:4700::1111");
props["https://doh.opendns.com/dns-query"] = std::string(
"208.67.222.222,208.67.220.220,2620:119:35::35, 2620:119:53::53");
proxy_->OnDoHProvidersChanged(props);
EXPECT_CALL(*resolver_,
SetDoHProviders(UnorderedElementsAre(
StrEq("https://dns.google.com"),
StrEq("https://doh.opendns.com/dns-query"),
StrEq("https://dns.quad9.net/dns-query")),
false));
SetNameServers({"8.8.8.8", "10.10.10.10"}, {"2620:fe::9", "2620:119:53::53"});
}
TEST_P(ProxyTest, MultipleDoHProvidersForSecureWithFallbackMode) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
SetNameServers({"1.1.1.1", "10.10.10.10"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(
*resolver_,
SetDoHProviders(UnorderedElementsAre(
StrEq("https://custom-provider-1.com"),
StrEq("https://custom-provider-2.com/dns-query")),
false));
brillo::VariantDictionary props;
props["https://custom-provider-1.com"] =
std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress);
props["https://custom-provider-2.com/dns-query"] =
std::string(shill::kDNSProxyDOHProvidersMatchAnyIPAddress);
proxy_->OnDoHProvidersChanged(props);
}
TEST_P(ProxyTest, DoHBadAlwaysOnConfigSetsAutomaticMode) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline));
SetNameServers({"1.1.1.1", "10.10.10.10"}, /*ipv6_nameservers=*/{});
EXPECT_CALL(
*resolver_,
SetDoHProviders(
ElementsAre(StrEq("https://chrome.cloudflare-dns.com/dns-query")),
false));
brillo::VariantDictionary props;
props["https://dns.opendns.com"] = std::string("");
props["https://dns.google.com"] = std::string("8.8.8.8, 8.8.4.4");
props["https://dns.quad9.net/dns-query"] = std::string("9.9.9.9,2620:fe::9");
props["https://chrome.cloudflare-dns.com/dns-query"] =
std::string("1.1.1.1,2606:4700:4700::1111");
props["https://doh.opendns.com/dns-query"] = std::string(
"208.67.222.222,208.67.220.220,2620:119:35::35, 2620:119:53::53");
proxy_->OnDoHProvidersChanged(props);
EXPECT_CALL(*resolver_,
SetDoHProviders(UnorderedElementsAre(
StrEq("https://dns.google.com"),
StrEq("https://doh.opendns.com/dns-query"),
StrEq("https://dns.quad9.net/dns-query")),
false));
SetNameServers({"8.8.8.8", "10.10.10.10"}, {"2620:fe::9", "2620:119:53::53"});
}
TEST_P(ProxyTest, SystemProxy_SetsDnsRedirectionRule) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
SetListenAddresses(ipv4_address_, ipv6_address_);
// System proxy requests a DnsRedirectionRule to exclude traffic destined
// not to the underlying network's name server.
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0");
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(
patchpanel::Client::DnsRedirectionRequestType::kExcludeDestination, _,
ipv4_address_.ToString(), _, _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(
patchpanel::Client::DnsRedirectionRequestType::kExcludeDestination, _,
ipv6_address_.ToString(), _, _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->OnDefaultDeviceChanged(dev.get());
// System proxy does not call patchpanel on Parallels VM started.
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kAdded,
virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap1",
"eth0"));
// System proxy does not call patchpanel on ARC started.
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kAdded,
virtualdev(patchpanel::Client::GuestType::kArcContainer, "arc_eth0",
"eth0"));
}
TEST_P(ProxyTest, SystemProxy_NeverListenForGuests) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
// System proxy does not listen for guests
EXPECT_CALL(*new_resolver, ListenUDP(_, _)).Times(0);
EXPECT_CALL(*new_resolver, ListenTCP(_, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kAdded,
virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap1",
"eth0"));
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kAdded,
virtualdev(patchpanel::Client::GuestType::kArcContainer, "arc_eth0",
"eth0"));
}
TEST_P(ProxyTest, DefaultProxy_SetDnsRedirectionRuleDeviceAlreadyStarted) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice());
SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"});
SetListenAddresses(ipv4_address_, ipv6_address_);
// Set DNS redirection rule.
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser,
_, _, ElementsAre("8.8.8.8"), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser,
_, _, ElementsAre("2001:4860:4860::8888"), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->ApplyDeviceUpdate();
EXPECT_EQ(proxy_->lifeline_fds_.size(), 2);
}
TEST_P(ProxyTest, DefaultProxy_SetDnsRedirectionRuleNewDeviceStarted) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault});
SetListenAddresses(ipv4_address_, ipv6_address_);
// Empty active device.
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
proxy_->ApplyDeviceUpdate();
EXPECT_EQ(proxy_->lifeline_fds_.size(), 0);
// Default device changed.
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0",
{"8.8.8.8"}, {"2001:4860:4860::8888"});
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser,
_, _, ElementsAre("8.8.8.8"), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser,
_, _, ElementsAre("2001:4860:4860::8888"), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->OnDefaultDeviceChanged(dev.get());
EXPECT_EQ(proxy_->lifeline_fds_.size(), 2);
}
TEST_P(ProxyTest, DefaultProxy_SetDnsRedirectionRuleGuest) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
SetInterfaceIPv6Address("vmtap0",
*net_base::IPv6Address::CreateFromString("fd00::1"));
// Guest started.
auto plugin_vm_dev =
virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap0", "eth0",
net_base::IPv4Address(192, 168, 1, 1));
net_base::IPv4Address addr4 =
proxy_->root_ns_enabled_ ? plugin_vm_dev.host_ipv4_addr : ipv4_address_;
net_base::IPv6Address addr6 =
proxy_->root_ns_enabled_
? *net_base::IPv6Address::CreateFromString("fd00::1")
: ipv6_address_;
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault,
"vmtap0", addr4.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault,
"vmtap0", addr6.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
plugin_vm_dev);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 2);
// Guest stopped.
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, plugin_vm_dev);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 0);
}
TEST_P(ProxyTest, DefaultProxy_ListenForGuests) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
// Guest started.
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, ListenUDP(_, "vmtap0")).WillOnce(Return(true));
EXPECT_CALL(*new_resolver, ListenTCP(_, "vmtap0")).WillOnce(Return(true));
} else {
EXPECT_CALL(*new_resolver, ListenUDP(_, _)).Times(0);
EXPECT_CALL(*new_resolver, ListenTCP(_, _)).Times(0);
}
auto plugin_vm_dev =
virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap0", "eth0");
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
plugin_vm_dev);
// Guest stopped.
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, StopListen(_, "vmtap0")).Times(2);
} else {
EXPECT_CALL(*new_resolver, StopListen(_, _)).Times(0);
}
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, plugin_vm_dev);
}
TEST_P(ProxyTest, DefaultProxy_NeverSetsDnsRedirectionRuleOtherGuest) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
// Other guest started.
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kAdded,
virtualdev(patchpanel::Client::GuestType::kArcContainer, "arc_eth0",
"eth0"));
}
TEST_P(ProxyTest, DefaultProxy_NeverListenForOtherGuests) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
// Other guest started.
EXPECT_CALL(*new_resolver, ListenUDP(_, _)).Times(0);
EXPECT_CALL(*new_resolver, ListenTCP(_, _)).Times(0);
auto arc_dev = virtualdev(patchpanel::Client::GuestType::kArcContainer,
"arc_eth0", "eth0");
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
arc_dev);
// Other guest stopped.
EXPECT_CALL(*new_resolver, StopListen(_, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, arc_dev);
}
TEST_P(ProxyTest, SystemProxy_SetDnsRedirectionRuleIPv6Added) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice());
SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"});
SetListenAddresses(ipv4_address_, /*ipv6_addr=*/std::nullopt);
// Test only applicable for specific network namespace.
if (proxy_->root_ns_enabled_) {
return;
}
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(
patchpanel::Client::DnsRedirectionRequestType::kExcludeDestination, _,
ipv6_address_.ToString(), _, _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
int ifindex = proxy_->IfNameToIndex(proxy_->ns_.peer_ifname.c_str());
net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress,
net_base::RTNLMessage::kModeAdd, 0 /* flags */,
0 /* seq */, 0 /* pid */, ifindex, AF_INET6);
msg.set_address_status(
net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
msg.SetAttribute(IFA_ADDRESS, ipv6_address_.ToBytes());
proxy_->RTNLMessageHandler(msg);
}
TEST_P(ProxyTest, SystemProxy_SetDnsRedirectionRuleIPv6Deleted) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice());
// Test only applicable for specific network namespace.
if (proxy_->root_ns_enabled_) {
return;
}
proxy_->lifeline_fds_.emplace(std::make_pair("", AF_INET6),
base::ScopedFD(make_fd()));
int ifindex = proxy_->IfNameToIndex(proxy_->ns_.peer_ifname.c_str());
net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress,
net_base::RTNLMessage::kModeDelete, 0 /* flags */,
0 /* seq */, 0 /* pid */, ifindex, AF_INET6);
msg.set_address_status(
net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
proxy_->RTNLMessageHandler(msg);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 0);
}
TEST_P(ProxyTest, DefaultProxy_SetDnsRedirectionRuleWithoutIPv6) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault});
SetListenAddresses(ipv4_address_, /*ipv6_addr=*/std::nullopt);
// Default device changed.
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0",
{"8.8.8.8"}, {"2001:4860:4860::8888"});
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser,
_, _, ElementsAre("8.8.8.8"), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->OnDefaultDeviceChanged(dev.get());
EXPECT_EQ(proxy_->lifeline_fds_.size(), 1);
// Guest started.
auto plugin_vm_dev =
virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap0", "eth0",
net_base::IPv4Address(192, 168, 1, 1));
net_base::IPv4Address addr =
proxy_->root_ns_enabled_ ? plugin_vm_dev.host_ipv4_addr : ipv4_address_;
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault,
"vmtap0", addr.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
plugin_vm_dev);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 2);
// Guest stopped.
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, plugin_vm_dev);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 1);
}
TEST_P(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Added) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice());
SetNameServers({"8.8.8.8"}, {"2001:4860:4860::8888"});
SetListenAddresses(ipv4_address_, /*ipv6_addr=*/std::nullopt);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
if (!proxy_->root_ns_enabled_) {
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kUser, _,
ipv6_address_.ToString(), _, _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
}
EXPECT_CALL(*patchpanel_client_, GetDevices())
.WillOnce(
Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev(
patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0")}));
EXPECT_CALL(
*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kDefault,
"vmtap0", ipv6_address_.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, ListenUDP(_, "vmtap0")).WillOnce(Return(true));
EXPECT_CALL(*new_resolver, ListenTCP(_, "vmtap0")).WillOnce(Return(true));
}
std::string ifname =
proxy_->root_ns_enabled_ ? "vmtap0" : proxy_->ns_.peer_ifname;
int ifindex = proxy_->IfNameToIndex(ifname.c_str());
net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress,
net_base::RTNLMessage::kModeAdd, 0 /* flags */,
0 /* seq */, 0 /* pid */, ifindex, AF_INET6);
int scope = proxy_->root_ns_enabled_ ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
msg.set_address_status(net_base::RTNLMessage::AddressStatus(0, 0, scope));
msg.SetAttribute(IFA_ADDRESS, ipv6_address_.ToBytes());
proxy_->RTNLMessageHandler(msg);
}
TEST_P(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Deleted) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice());
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
proxy_->lifeline_fds_.emplace(std::make_pair("", AF_INET6),
base::ScopedFD(make_fd()));
proxy_->lifeline_fds_.emplace(std::make_pair("vmtap0", AF_INET6),
base::ScopedFD(make_fd()));
EXPECT_CALL(*patchpanel_client_, GetDevices())
.WillOnce(
Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev(
patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0")}));
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, StopListen(AF_INET6, "vmtap0")).Times(1);
}
std::string ifname =
proxy_->root_ns_enabled_ ? "vmtap0" : proxy_->ns_.peer_ifname;
int ifindex = proxy_->IfNameToIndex(ifname.c_str());
net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress,
net_base::RTNLMessage::kModeDelete, 0 /* flags */,
0 /* seq */, 0 /* pid */, ifindex, AF_INET6);
int scope = proxy_->root_ns_enabled_ ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
msg.set_address_status(net_base::RTNLMessage::AddressStatus(0, 0, scope));
proxy_->RTNLMessageHandler(msg);
if (proxy_->root_ns_enabled_) {
EXPECT_EQ(proxy_->lifeline_fds_.size(), 1);
} else {
EXPECT_EQ(proxy_->lifeline_fds_.size(), 0);
}
}
TEST_P(ProxyTest, DefaultProxy_SetDnsRedirectionRuleUnrelatedIPv6Added) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault},
ShillDevice());
EXPECT_CALL(*patchpanel_client_, GetDevices())
.WillRepeatedly(
Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev(
patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0")}));
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
net_base::RTNLMessage msg_unrelated_ifindex(
net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
AF_INET6);
msg_unrelated_ifindex.set_address_status(
net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
msg_unrelated_ifindex.SetAttribute(IFA_ADDRESS, ipv6_address_.ToBytes());
proxy_->RTNLMessageHandler(msg_unrelated_ifindex);
net_base::RTNLMessage msg_unrelated_scope(
net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
AF_INET6);
msg_unrelated_scope.set_address_status(
net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_LINK));
msg_unrelated_scope.SetAttribute(IFA_ADDRESS, ipv6_address_.ToBytes());
proxy_->RTNLMessageHandler(msg_unrelated_scope);
}
TEST_P(ProxyTest, ArcProxy_SetDnsRedirectionRuleDeviceAlreadyStarted) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice());
SetListenAddresses(ipv4_address_, ipv6_address_);
SetInterfaceIPv6Address("arc_eth0",
*net_base::IPv6Address::CreateFromString("fd00::1"));
net_base::IPv4Address addr4 = proxy_->root_ns_enabled_
? net_base::IPv4Address(192, 168, 1, 1)
: ipv4_address_;
net_base::IPv6Address addr6 =
proxy_->root_ns_enabled_
? *net_base::IPv6Address::CreateFromString("fd00::1")
: ipv6_address_;
// Set devices created before the proxy started.
EXPECT_CALL(*patchpanel_client_, GetDevices())
.WillOnce(Return(std::vector<patchpanel::Client::VirtualDevice>{
virtualdev(patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0",
net_base::IPv4Address(192, 168, 1, 1))}));
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc,
"arc_eth0", addr4.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc,
"arc_eth0", addr6.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->ApplyDeviceUpdate();
EXPECT_EQ(proxy_->lifeline_fds_.size(), 2);
}
TEST_P(ProxyTest, ArcProxy_SetDnsRedirectionRuleNewDeviceStarted) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice());
SetListenAddresses(ipv4_address_, ipv6_address_);
SetInterfaceIPv6Address("arc_eth0",
*net_base::IPv6Address::CreateFromString("fd00::1"));
// Guest started.
auto arc_dev =
virtualdev(patchpanel::Client::GuestType::kArcContainer, "arc_eth0",
"eth0", net_base::IPv4Address(192, 168, 1, 1));
net_base::IPv4Address addr4 =
proxy_->root_ns_enabled_ ? arc_dev.host_ipv4_addr : ipv4_address_;
net_base::IPv6Address addr6 =
proxy_->root_ns_enabled_
? *net_base::IPv6Address::CreateFromString("fd00::1")
: ipv6_address_;
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc,
"arc_eth0", addr4.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc,
"arc_eth0", addr6.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
arc_dev);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 2);
// Guest stopped.
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, arc_dev);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 0);
}
TEST_P(ProxyTest, ArcProxy_ListenForGuests) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
// Guest started.
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, ListenUDP(_, "arc_eth0")).WillOnce(Return(true));
EXPECT_CALL(*new_resolver, ListenTCP(_, "arc_eth0")).WillOnce(Return(true));
} else {
EXPECT_CALL(*new_resolver, ListenUDP(_, _)).Times(0);
EXPECT_CALL(*new_resolver, ListenTCP(_, _)).Times(0);
}
auto arc_dev = virtualdev(patchpanel::Client::GuestType::kArcContainer,
"arc_eth0", "eth0");
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
arc_dev);
// Guest stopped.
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, StopListen(_, "arc_eth0")).Times(2);
} else {
EXPECT_CALL(*new_resolver, StopListen(_, _)).Times(0);
}
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, arc_dev);
}
TEST_P(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherGuest) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice());
proxy_->ipv6_address_ = ipv6_address_;
// Other guest started.
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kAdded,
virtualdev(patchpanel::Client::GuestType::kTerminaVm, "vmtap0", "eth0"));
}
TEST_P(ProxyTest, ArcProxy_NeverListenForOtherGuests) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
// Other guest started.
EXPECT_CALL(*new_resolver, ListenUDP(_, _)).Times(0);
EXPECT_CALL(*new_resolver, ListenTCP(_, _)).Times(0);
auto plugin_vm_dev =
virtualdev(patchpanel::Client::GuestType::kParallelsVm, "vmtap0", "eth0");
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
plugin_vm_dev);
// Other guest stopped.
EXPECT_CALL(*new_resolver, StopListen(_, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, plugin_vm_dev);
}
TEST_P(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherIfname) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"});
proxy_->device_ = ShillDevice();
SetListenAddresses(ipv4_address_, ipv6_address_);
// ARC guest with other interface started.
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kAdded,
virtualdev(patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0"));
}
TEST_P(ProxyTest, ArcProxy_NeverListenForOtherIfname) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"},
ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kWifi, "wlan0"));
SetListenAddresses(ipv4_address_, ipv6_address_);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
// Other guest started.
EXPECT_CALL(*new_resolver, ListenUDP(_, _)).Times(0);
EXPECT_CALL(*new_resolver, ListenTCP(_, _)).Times(0);
auto arc_dev = virtualdev(patchpanel::Client::GuestType::kArcContainer,
"arc_eth0", "eth0");
proxy_->OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent::kAdded,
arc_dev);
// Other guest stopped.
EXPECT_CALL(*new_resolver, StopListen(_, _)).Times(0);
proxy_->OnVirtualDeviceChanged(
patchpanel::Client::VirtualDeviceEvent::kRemoved, arc_dev);
}
TEST_P(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Added) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice());
SetListenAddresses(ipv4_address_, /*ipv6_addr=*/std::nullopt);
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
EXPECT_CALL(*patchpanel_client_, GetDevices())
.WillOnce(
Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev(
patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")}));
EXPECT_CALL(*patchpanel_client_,
RedirectDns(patchpanel::Client::DnsRedirectionRequestType::kArc,
"arc_eth0", ipv6_address_.ToString(), IsEmpty(), _))
.WillOnce(Return(ByMove(base::ScopedFD(make_fd()))));
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, ListenUDP(_, "arc_eth0")).WillOnce(Return(true));
EXPECT_CALL(*new_resolver, ListenTCP(_, "arc_eth0")).WillOnce(Return(true));
}
std::string ifname =
proxy_->root_ns_enabled_ ? "arc_eth0" : proxy_->ns_.peer_ifname;
int ifindex = proxy_->IfNameToIndex(ifname.c_str());
net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress,
net_base::RTNLMessage::kModeAdd, 0 /* flags */,
0 /* seq */, 0 /* pid */, ifindex, AF_INET6);
int scope = proxy_->root_ns_enabled_ ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
msg.set_address_status(net_base::RTNLMessage::AddressStatus(0, 0, scope));
msg.SetAttribute(IFA_ADDRESS, ipv6_address_.ToBytes());
proxy_->RTNLMessageHandler(msg);
}
TEST_P(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Deleted) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice());
auto* new_resolver = new MockResolver();
proxy_->resolver_ = base::WrapUnique(new_resolver);
proxy_->lifeline_fds_.emplace(std::make_pair("arc_eth0", AF_INET6),
base::ScopedFD(make_fd()));
EXPECT_CALL(*patchpanel_client_, GetDevices())
.WillOnce(
Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev(
patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")}));
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*new_resolver, StopListen(AF_INET6, "arc_eth0")).Times(1);
}
std::string ifname =
proxy_->root_ns_enabled_ ? "arc_eth0" : proxy_->ns_.peer_ifname;
int ifindex = proxy_->IfNameToIndex(ifname.c_str());
net_base::RTNLMessage msg(net_base::RTNLMessage::kTypeAddress,
net_base::RTNLMessage::kModeDelete, 0 /* flags */,
0 /* seq */, 0 /* pid */, ifindex, AF_INET6);
int scope = proxy_->root_ns_enabled_ ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
msg.set_address_status(net_base::RTNLMessage::AddressStatus(0, 0, scope));
proxy_->RTNLMessageHandler(msg);
EXPECT_EQ(proxy_->lifeline_fds_.size(), 0);
}
TEST_P(ProxyTest, ArcProxy_SetDnsRedirectionRuleUnrelatedIPv6Added) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "eth0"},
ShillDevice());
EXPECT_CALL(*patchpanel_client_, GetDevices())
.WillRepeatedly(
Return(std::vector<patchpanel::Client::VirtualDevice>{virtualdev(
patchpanel::Client::GuestType::kArcVm, "arc_eth0", "eth0")}));
EXPECT_CALL(*patchpanel_client_, RedirectDns(_, _, _, _, _)).Times(0);
net_base::RTNLMessage msg_unrelated_ifindex(
net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
AF_INET6);
msg_unrelated_ifindex.set_address_status(
net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_UNIVERSE));
msg_unrelated_ifindex.SetAttribute(IFA_ADDRESS, ipv6_address_.ToBytes());
proxy_->RTNLMessageHandler(msg_unrelated_ifindex);
net_base::RTNLMessage msg_unrelated_scope(
net_base::RTNLMessage::kTypeAddress, net_base::RTNLMessage::kModeAdd,
0 /* flags */, 0 /* seq */, 0 /* pid */, -1 /* interface_index */,
AF_INET6);
msg_unrelated_scope.set_address_status(
net_base::RTNLMessage::AddressStatus(0, 0, RT_SCOPE_LINK));
msg_unrelated_scope.SetAttribute(IFA_ADDRESS, ipv6_address_.ToBytes());
proxy_->RTNLMessageHandler(msg_unrelated_scope);
}
TEST_P(ProxyTest, UpdateNameServers) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem},
ShillDevice());
proxy_->device_->network_config.dns_servers = StringsToIPAddressesChecked(
{// IPv4 name servers.
"8.8.8.8", "192.168.1.1",
// IPv6 name servers.
"eeb0:117e:92ee:ad3d:ce0d:a646:95ea:a16e", "::2"});
proxy_->UpdateNameServers();
const std::vector<net_base::IPv4Address> expected_ipv4_dns_addresses = {
net_base::IPv4Address(8, 8, 8, 8), net_base::IPv4Address(192, 168, 1, 1)};
const std::vector<net_base::IPv6Address> expected_ipv6_dns_addresses = {
*net_base::IPv6Address::CreateFromString(
"eeb0:117e:92ee:ad3d:ce0d:a646:95ea:a16e"),
*net_base::IPv6Address::CreateFromString("::2")};
EXPECT_THAT(proxy_->doh_config_.ipv4_nameservers(),
expected_ipv4_dns_addresses);
EXPECT_THAT(proxy_->doh_config_.ipv6_nameservers(),
expected_ipv6_dns_addresses);
}
TEST_P(ProxyTest, DomainDoHConfigsUpdate) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault});
std::vector<std::string> props = {"domain1.com", "domain2.net"};
proxy_->OnDoHIncludedDomainsChanged(props);
proxy_->OnDoHExcludedDomainsChanged(props);
}
TEST_P(ProxyTest, DomainDoHConfigsUpdate_ProxyStopped) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault});
proxy_->Stop();
std::vector<std::string> props = {"domain1.com", "domain2.net"};
proxy_->OnDoHIncludedDomainsChanged(props);
proxy_->OnDoHExcludedDomainsChanged(props);
}
TEST_P(ProxyTest, ArcProxy_SetInterface) {
SetUpProxy(GetParam(),
Proxy::Options{.type = Proxy::Type::kARC, .ifname = "wlan0"});
SetListenAddresses(ipv4_address_, ipv6_address_);
auto wifi = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kWifi, "wlan0",
{"8.8.8.8"}, {"2001:4860:4860::8888"});
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*resolver_, SetInterface("wlan0"));
} else {
EXPECT_CALL(*resolver_, SetInterface).Times(0);
}
proxy_->OnDeviceChanged(wifi.get());
}
TEST_P(ProxyTest, DefaultProxy_SetInterface) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kDefault});
SetListenAddresses(ipv4_address_, ipv6_address_);
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0",
{"8.8.8.8", "8.8.4.4"},
{"2001:4860:4860::8888", "2001:4860:4860::8844"});
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*resolver_, SetInterface("eth0"));
} else {
EXPECT_CALL(*resolver_, SetInterface).Times(0);
}
proxy_->OnDefaultDeviceChanged(dev.get());
auto vpn = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kVPN);
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*resolver_, ClearInterface);
} else {
EXPECT_CALL(*resolver_, SetInterface).Times(0);
}
proxy_->OnDefaultDeviceChanged(vpn.get());
}
TEST_P(ProxyTest, SystemProxy_SetInterface) {
SetUpProxy(GetParam(), Proxy::Options{.type = Proxy::Type::kSystem});
SetListenAddresses(ipv4_address_, ipv6_address_);
auto dev = ShillDevice(shill::Client::Device::ConnectionState::kOnline,
shill::Client::Device::Type::kEthernet, "eth0",
{"8.8.8.8", "8.8.4.4"},
{"2001:4860:4860::8888", "2001:4860:4860::8844"});
if (proxy_->root_ns_enabled_) {
EXPECT_CALL(*resolver_, SetInterface("eth0"));
} else {
EXPECT_CALL(*resolver_, SetInterface).Times(0);
}
proxy_->OnDefaultDeviceChanged(dev.get());
}
} // namespace dns_proxy