blob: da9567cbb8714d7fc30383ce3d22f4d62f2f137e [file] [log] [blame] [edit]
// 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 "shill/vpn/wireguard_driver.h"
#include <cstddef>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include <base/containers/flat_set.h>
#include <base/files/scoped_file.h>
#include <base/files/scoped_temp_dir.h>
#include <base/files/file_util.h>
#include <chromeos/dbus/service_constants.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <net-base/ip_address.h>
#include <net-base/ipv4_address.h>
#include <net-base/ipv6_address.h>
#include <net-base/mock_process_manager.h>
#include <net-base/process_manager.h>
#include "shill/metrics.h"
#include "shill/mock_control.h"
#include "shill/mock_device_info.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/store/fake_store.h"
#include "shill/store/property_store.h"
#include "shill/test_event_dispatcher.h"
#include "shill/vpn/fake_vpn_util.h"
#include "shill/vpn/mock_vpn_driver.h"
#include "shill/vpn/vpn_end_reason.h"
#include "shill/vpn/vpn_provider.h"
namespace shill {
using testing::_;
using testing::AllOf;
using testing::DoAll;
using testing::HasSubstr;
using testing::Mock;
using testing::NiceMock;
using testing::Not;
using testing::Return;
using testing::WithArg;
// Expose necessary private members of WireGuardDriver for testing.
class WireGuardDriverTestPeer {
public:
explicit WireGuardDriverTestPeer(WireGuardDriver* driver) : driver_(driver) {
driver->vpn_util_ = std::make_unique<FakeVPNUtil>();
}
const Stringmaps& peers() { return driver_->peers_; }
const base::ScopedFD& config_fd() { return driver_->config_fd_; }
private:
std::unique_ptr<WireGuardDriver> driver_;
};
namespace {
constexpr pid_t kWireGuardToolsPid = 12346;
constexpr char kIfName[] = "wg0";
constexpr int kIfIndex = 123;
// Randomly generated key for testing.
constexpr char kPrivateKey1[] = "gOL/kVF88Mdr7rVM2Fz91UgyAW4L8iYogU/M+9hlKmM=";
constexpr char kPrivateKey2[] = "wARBVZOPBWo7OoyHLfv2mDgFxYJ3S6uc9lIOpRiGqVI=";
// Consistent with the properties set in
// WireGuardDriverTest::InitializePropertyStore().
constexpr char kExpectedConfigFileContents[] = R"([Interface]
PrivateKey=gOL/kVF88Mdr7rVM2Fz91UgyAW4L8iYogU/M+9hlKmM=
FwMark=0x4500
[Peer]
PublicKey=public-key-1
PresharedKey=preshared-key-1
Endpoint=10.0.1.1:12345
AllowedIPs=192.168.1.2/32,192.168.2.0/24
PersistentKeepalive=10
[Peer]
PublicKey=public-key-2
Endpoint=10.0.1.2:12345
AllowedIPs=192.168.1.2/32,fd00:0:0:0:1::/128
[Peer]
PublicKey=public-key-3
Endpoint=10.0.1.3:12345
AllowedIPs=fd00:0:0:0:1::/128,fd00:0:0:2::/64
)";
// `wg show` use tabs in its output, so the tabs in this raw string are
// by intention.
constexpr char kWgShowDumpOutput[] = R"(ignore-first-line
public-key-1 preshared-key endpoint allowed-ips 1234 1 2 0
public-key-2 preshared-key endpoint allowed-ips 1235 3 4 0
)";
static constexpr char kIPv4Address[] = "10.12.14.2";
static constexpr char kIPv6Address1[] = "fd00:0:0:0:1::";
static constexpr char kIPv6Address2[] = "fd00:0:0:2::";
static constexpr char kWrongIPAddress[] = "10.12.14..2";
constexpr char kWireGuardIPAddress[] = "WireGuard.IPAddress";
class WireGuardDriverTest : public testing::Test {
public:
WireGuardDriverTest() : manager_(&control_, &dispatcher_, &metrics_) {
ResetDriver();
SetFakeKeyGenerator();
}
protected:
// Useful in storage-related tests.
void ResetDriver() {
driver_ = new WireGuardDriver(&manager_, &process_manager_);
driver_test_peer_ = std::make_unique<WireGuardDriverTestPeer>(driver_);
property_store_ = std::make_unique<PropertyStore>();
driver_->InitPropertyStore(property_store_.get());
}
void InitializePropertyStore() {
Error err;
property_store_->SetStringProperty(kWireGuardPrivateKey, kPrivateKey1,
&err);
property_store_->SetStringsProperty(
kWireGuardIPAddress,
std::vector<std::string>{kIPv4Address, kIPv6Address1}, &err);
property_store_->SetStringmapsProperty(
kWireGuardPeers,
{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPresharedKey, "preshared-key-1"},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,192.168.2.0/24"}},
{{kWireGuardPeerPublicKey, "public-key-2"},
{kWireGuardPeerEndpoint, "10.0.1.2:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,fd00:0:0:0:1::/128"}},
{{kWireGuardPeerPublicKey, "public-key-3"},
{kWireGuardPeerEndpoint, "10.0.1.3:12345"},
{kWireGuardPeerAllowedIPs, "fd00:0:0:0:1::/128,fd00:0:0:2::/64"}},
},
&err);
}
// Whenever the driver asks for calculating a public key, just echoes the
// input back.
// TODO(jiejiang): Consider returning a different key to avoid missing the
// failure that keys are misused.
void SetFakeKeyGenerator() {
EXPECT_CALL(
process_manager_,
StartProcessInMinijailWithPipes(
_, base::FilePath("/usr/bin/wg"),
std::vector<std::string>{"pubkey"}, _,
AllOf(
net_base::MinijailOptionsMatchUserGroup("vpn", "vpn"),
net_base::MinijailOptionsMatchCapMask(0u),
net_base::MinijailOptionsMatchInheritSupplementaryGroup(true)),
_, _))
.WillRepeatedly([](const base::Location&, const base::FilePath&,
const std::vector<std::string>&,
const std::map<std::string, std::string>&,
const net_base::ProcessManager::MinijailOptions&,
base::OnceCallback<void(int)>,
struct net_base::std_file_descriptors std_fds) {
CHECK(std_fds.stdin_fd);
CHECK(std_fds.stdout_fd);
int echo_pipe[2];
CHECK_EQ(pipe(echo_pipe), 0);
*std_fds.stdin_fd = echo_pipe[1];
*std_fds.stdout_fd = echo_pipe[0];
return 0;
});
}
void InvokeConnectAsyncKernel() {
driver_->ConnectAsync(&driver_event_handler_);
EXPECT_CALL(*device_info(), CreateWireGuardInterface(kIfName, _, _))
.WillOnce([this](const std::string&,
DeviceInfo::LinkReadyCallback link_ready_cb,
base::OnceClosure failure_cb) {
this->link_ready_callback_ = std::move(link_ready_cb);
this->create_kernel_link_failed_callback_ = std::move(failure_cb);
return true;
});
dispatcher_.DispatchPendingEvents();
}
void InvokeLinkReady() {
// wireguard-tools should be invoked on interface ready.
std::vector<std::string> args;
base::flat_set<int> fds;
constexpr uint64_t kExpectedCapMask = CAP_TO_MASK(CAP_NET_ADMIN);
EXPECT_CALL(
process_manager_,
StartProcessInMinijail(
_, base::FilePath("/usr/bin/wg"), _, _,
AllOf(
net_base::MinijailOptionsMatchUserGroup("vpn", "vpn"),
net_base::MinijailOptionsMatchCapMask(kExpectedCapMask),
net_base::MinijailOptionsMatchInheritSupplementaryGroup(true)),
_))
.WillOnce([this, &args, &fds](
const base::Location&, const base::FilePath&,
const std::vector<std::string>& arguments,
const std::map<std::string, std::string>&,
const net_base::MockProcessManager::MinijailOptions&
minijail_options,
base::OnceCallback<void(int)> exit_callback) {
wireguard_tools_exit_callback_ = std::move(exit_callback);
args = arguments;
fds = minijail_options.preserved_nonstd_fds;
return kWireGuardToolsPid;
});
std::move(link_ready_callback_).Run(kIfName, kIfIndex);
EXPECT_EQ(args[0], "setconf");
EXPECT_EQ(args[1], kIfName);
EXPECT_EQ(fds.size(), 1);
EXPECT_EQ(args[2], base::StringPrintf("/proc/self/fd/%d", *fds.begin()));
config_file_path_ = base::FilePath(args[2]);
EXPECT_TRUE(base::PathExists(config_file_path_));
}
// Set expectation to call `wg show wg0 dump`.
void ExpectExecuteWgShow() {
constexpr uint64_t kExpectedCapMask = CAP_TO_MASK(CAP_NET_ADMIN);
EXPECT_CALL(
process_manager_,
StartProcessInMinijailWithStdout(
_, base::FilePath("/usr/bin/wg"),
std::vector<std::string>({"show", kIfName, "dump"}), _,
AllOf(
net_base::MinijailOptionsMatchUserGroup("vpn", "vpn"),
net_base::MinijailOptionsMatchCapMask(kExpectedCapMask),
net_base::MinijailOptionsMatchInheritSupplementaryGroup(true)),
_))
.WillOnce(WithArg<5>(
[this](net_base::ProcessManager::ExitWithStdoutCallback callback) {
wg_show_callback_ = std::move(callback);
return 0;
}));
}
void ExpectCallMetrics(Metrics::VpnWireGuardKeyPairSource key_pair_source,
int peers_num,
Metrics::VpnWireGuardAllowedIPsType allowed_ips_type) {
EXPECT_CALL(metrics_,
SendEnumToUMA(Metrics::kMetricVpnWireGuardKeyPairSource,
key_pair_source));
EXPECT_CALL(metrics_,
SendToUMA(Metrics::kMetricVpnWireGuardPeersNum, peers_num));
EXPECT_CALL(metrics_,
SendEnumToUMA(Metrics::kMetricVpnWireGuardAllowedIPsType,
allowed_ips_type));
}
MockDeviceInfo* device_info() { return manager_.mock_device_info(); }
MockControl control_;
EventDispatcherForTest dispatcher_;
MockMetrics metrics_;
net_base::MockProcessManager process_manager_;
MockManager manager_;
FakeStore fake_store_;
std::unique_ptr<PropertyStore> property_store_;
MockVPNDriverEventHandler driver_event_handler_;
WireGuardDriver* driver_; // owned by driver_test_peer_
std::unique_ptr<WireGuardDriverTestPeer> driver_test_peer_;
base::OnceCallback<void(int)> wireguard_exit_callback_;
base::OnceCallback<void(int)> wireguard_tools_exit_callback_;
DeviceInfo::LinkReadyCallback link_ready_callback_;
base::OnceClosure create_kernel_link_failed_callback_;
base::FilePath config_file_path_;
net_base::ProcessManager::ExitWithStdoutCallback wg_show_callback_;
};
TEST_F(WireGuardDriverTest, VPNType) {
EXPECT_EQ(driver_->vpn_type(), VPNType::kWireGuard);
}
TEST_F(WireGuardDriverTest, ConnectFlowKernel) {
InitializePropertyStore();
InvokeConnectAsyncKernel();
InvokeLinkReady();
// Checks config file content.
std::string contents;
CHECK(base::ReadFileToString(config_file_path_, &contents));
EXPECT_EQ(contents, kExpectedConfigFileContents);
// Configuration done.
EXPECT_CALL(driver_event_handler_, OnDriverConnected(kIfName, kIfIndex));
std::move(wireguard_tools_exit_callback_).Run(0);
// Checks that the config file has been deleted.
EXPECT_FALSE(driver_test_peer_->config_fd().is_valid());
// Checks the network configuration.
const auto network_config = driver_->GetNetworkConfig();
ASSERT_NE(network_config, nullptr);
EXPECT_EQ(network_config->ipv4_address,
net_base::IPv4CIDR::CreateFromStringAndPrefix(kIPv4Address, 32));
EXPECT_EQ(network_config->ipv6_addresses.size(), 1);
EXPECT_EQ(network_config->ipv6_addresses[0],
net_base::IPv6CIDR::CreateFromStringAndPrefix(kIPv6Address1, 128));
EXPECT_THAT(network_config->included_route_prefixes,
testing::UnorderedElementsAre(
// We do not dedup so "192.168.1.2/32" and
// "fd00:0:0:0:1::/128" appears twice.
net_base::IPCIDR::CreateFromCIDRString("192.168.1.2/32"),
net_base::IPCIDR::CreateFromCIDRString("192.168.1.2/32"),
net_base::IPCIDR::CreateFromCIDRString("192.168.2.0/24"),
net_base::IPCIDR::CreateFromCIDRString("fd00:0:0:0:1::/128"),
net_base::IPCIDR::CreateFromCIDRString("fd00:0:0:0:1::/128"),
net_base::IPCIDR::CreateFromCIDRString("fd00:0:0:2::/64")));
// Disconnect.
EXPECT_CALL(*device_info(), DeleteInterface(kIfIndex));
driver_->Disconnect();
}
TEST_F(WireGuardDriverTest, ReadLinkStatus) {
InitializePropertyStore();
InvokeConnectAsyncKernel();
InvokeLinkReady();
std::move(wireguard_tools_exit_callback_).Run(0);
ExpectExecuteWgShow();
dispatcher_.task_environment().FastForwardBy(base::Seconds(10));
ASSERT_TRUE(wg_show_callback_);
std::move(wg_show_callback_).Run(0, kWgShowDumpOutput);
auto peers = driver_test_peer_->peers();
// Assume the ordering of the peers is expected here.
ASSERT_GE(peers.size(), 2);
EXPECT_EQ(peers[0][kWireGuardPeerPublicKey], "public-key-1");
EXPECT_EQ(peers[0][kWireGuardPeerLatestHandshake], "1234");
EXPECT_EQ(peers[0][kWireGuardPeerRxBytes], "1");
EXPECT_EQ(peers[0][kWireGuardPeerTxBytes], "2");
EXPECT_EQ(peers[1][kWireGuardPeerPublicKey], "public-key-2");
EXPECT_EQ(peers[1][kWireGuardPeerLatestHandshake], "1235");
EXPECT_EQ(peers[1][kWireGuardPeerRxBytes], "3");
EXPECT_EQ(peers[1][kWireGuardPeerTxBytes], "4");
// Check that LastReadLinkStatusTime is set in Provider dict.
KeyValueStore provider;
Error err;
ASSERT_TRUE(property_store_->GetKeyValueStoreProperty(kProviderProperty,
&provider, &err));
EXPECT_TRUE(provider.Contains<std::string>(kWireGuardLastReadLinkStatusTime));
// Check that `wg show` will be executed again after the given time interval
// passed.
ExpectExecuteWgShow();
dispatcher_.task_environment().FastForwardBy(base::Minutes(1));
// Disconnect the driver, check that the status fields have been cleared.
driver_->Disconnect();
ASSERT_TRUE(property_store_->GetKeyValueStoreProperty(kProviderProperty,
&provider, &err));
EXPECT_FALSE(
provider.Contains<std::string>(kWireGuardLastReadLinkStatusTime));
for (const auto& peer : driver_test_peer_->peers()) {
EXPECT_FALSE(base::Contains(peer, kWireGuardPeerLatestHandshake));
EXPECT_FALSE(base::Contains(peer, kWireGuardPeerRxBytes));
EXPECT_FALSE(base::Contains(peer, kWireGuardPeerTxBytes));
}
}
TEST_F(WireGuardDriverTest, WireGuardToolsFailed) {
InitializePropertyStore();
InvokeConnectAsyncKernel();
InvokeLinkReady();
// Configuration failed.
EXPECT_CALL(driver_event_handler_, OnDriverFailure(_, _));
std::move(wireguard_tools_exit_callback_).Run(1);
// Checks that the config file has been deleted.
EXPECT_FALSE(driver_test_peer_->config_fd().is_valid());
}
TEST_F(WireGuardDriverTest, PropertyStoreAndConfigFile) {
std::string contents;
Error err;
InitializePropertyStore();
// Save & load should not lose any information.
const std::string kStorageId = "wireguard-test";
driver_->Save(&fake_store_, kStorageId, /*save_credentials=*/true);
ResetDriver();
driver_->Load(&fake_store_, kStorageId);
InvokeConnectAsyncKernel();
InvokeLinkReady();
CHECK(base::ReadFileToString(config_file_path_, &contents));
EXPECT_EQ(contents, kExpectedConfigFileContents);
ExpectCallMetrics(Metrics::kVpnWireGuardKeyPairSourceUserInput, 3,
Metrics::kVpnWireGuardAllowedIPsTypeNoDefaultRoute);
std::move(wireguard_tools_exit_callback_).Run(0);
Mock::VerifyAndClearExpectations(&metrics_);
driver_->Disconnect();
// Checks reading properties. Private keys and preshared keys should not be
// readable.
KeyValueStore provider;
EXPECT_TRUE(property_store_->GetKeyValueStoreProperty(kProviderProperty,
&provider, &err));
EXPECT_FALSE(provider.Contains<std::string>(kWireGuardPrivateKey));
EXPECT_TRUE(provider.Contains<std::string>(kWireGuardPublicKey));
EXPECT_EQ(provider.Get<Strings>(kWireGuardIPAddress),
(std::vector<std::string>{kIPv4Address, kIPv6Address1}));
EXPECT_EQ(
provider.Get<Stringmaps>(kWireGuardPeers),
(Stringmaps{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,192.168.2.0/24"}},
{{kWireGuardPeerPublicKey, "public-key-2"},
{kWireGuardPeerPersistentKeepalive, ""},
{kWireGuardPeerEndpoint, "10.0.1.2:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,fd00:0:0:0:1::/128"}},
{{kWireGuardPeerPublicKey, "public-key-3"},
{kWireGuardPeerPersistentKeepalive, ""},
{kWireGuardPeerEndpoint, "10.0.1.3:12345"},
{kWireGuardPeerAllowedIPs, "fd00:0:0:0:1::/128,fd00:0:0:2::/64"}},
}));
// Setting peers without touching PresharedKey property should leave it
// unchanged.
property_store_->SetStringmapsProperty(
kWireGuardPeers,
{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,192.168.2.0/24"}},
{{kWireGuardPeerPublicKey, "public-key-2"},
{kWireGuardPeerEndpoint, "10.0.1.2:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,fd00:0:0:0:1::/128"}},
{{kWireGuardPeerPublicKey, "public-key-3"},
{kWireGuardPeerEndpoint, "10.0.1.3:12345"},
{kWireGuardPeerAllowedIPs, "fd00:0:0:0:1::/128,fd00:0:0:2::/64"}},
},
&err);
InvokeConnectAsyncKernel();
InvokeLinkReady();
CHECK(base::ReadFileToString(config_file_path_, &contents));
EXPECT_EQ(contents, kExpectedConfigFileContents);
driver_->Disconnect();
// Setting peers with an empty PublicKey property should be rejected and the
// current peers will not be modified.
property_store_->SetStringmapsProperty(
kWireGuardPeers,
{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,192.168.2.0/24"}},
{{kWireGuardPeerPublicKey, ""},
{kWireGuardPeerEndpoint, "10.0.1.2:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,fd00:0:0:0:1::/128"}},
},
&err);
EXPECT_TRUE(err.IsFailure());
InvokeConnectAsyncKernel();
InvokeLinkReady();
CHECK(base::ReadFileToString(config_file_path_, &contents));
EXPECT_EQ(contents, kExpectedConfigFileContents);
driver_->Disconnect();
// Setting peers with a duplicated PublicKey property should be rejected and
// the current peers will not be modified.
property_store_->SetStringmapsProperty(
kWireGuardPeers,
{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,192.168.2.0/24"}},
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerEndpoint, "10.0.1.2:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,fd00:0:0:0:1::/128"}},
},
&err);
EXPECT_TRUE(err.IsFailure());
InvokeConnectAsyncKernel();
InvokeLinkReady();
CHECK(base::ReadFileToString(config_file_path_, &contents));
EXPECT_EQ(contents, kExpectedConfigFileContents);
driver_->Disconnect();
// Setting peers with an empty PresharedKey property should clear the
// PresharedKey of that peer.
property_store_->SetStringmapsProperty(
kWireGuardPeers,
{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPresharedKey, ""},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,192.168.2.0/24"}},
{{kWireGuardPeerPublicKey, "public-key-2"},
{kWireGuardPeerEndpoint, "10.0.1.2:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,fd00:0:0:0:1::/128"}},
},
&err);
InvokeConnectAsyncKernel();
InvokeLinkReady();
CHECK(base::ReadFileToString(config_file_path_, &contents));
EXPECT_THAT(contents, Not(HasSubstr("PresharedKey=")));
// Clears the private key, changes the number of peers, and add 0.0.0.0/0 as
// its allowed ips. The reported metrics should be changed.
property_store_->SetStringProperty(kWireGuardPrivateKey, "", &err);
property_store_->SetStringmapsProperty(
kWireGuardPeers,
{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPresharedKey, ""},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "0.0.0.0/0"}},
},
&err);
driver_->Save(&fake_store_, kStorageId, /*save_credentials=*/true);
InvokeConnectAsyncKernel();
InvokeLinkReady();
ExpectCallMetrics(Metrics::kVpnWireGuardKeyPairSourceSoftwareGenerated, 1,
Metrics::kVpnWireGuardAllowedIPsTypeHasDefaultRoute);
std::move(wireguard_tools_exit_callback_).Run(0);
Mock::VerifyAndClearExpectations(&metrics_);
}
TEST_F(WireGuardDriverTest, UnloadCredentials) {
InitializePropertyStore();
driver_->UnloadCredentials();
const auto args = driver_->const_args();
EXPECT_FALSE(args->Contains<std::string>(kWireGuardPrivateKey));
EXPECT_EQ(
driver_test_peer_->peers(),
(Stringmaps{
{{kWireGuardPeerPublicKey, "public-key-1"},
{kWireGuardPeerPresharedKey, ""},
{kWireGuardPeerPersistentKeepalive, "10"},
{kWireGuardPeerEndpoint, "10.0.1.1:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,192.168.2.0/24"}},
{{kWireGuardPeerPublicKey, "public-key-2"},
{kWireGuardPeerPresharedKey, ""},
{kWireGuardPeerEndpoint, "10.0.1.2:12345"},
{kWireGuardPeerAllowedIPs, "192.168.1.2/32,fd00:0:0:0:1::/128"}},
{{kWireGuardPeerPublicKey, "public-key-3"},
{kWireGuardPeerPresharedKey, ""},
{kWireGuardPeerEndpoint, "10.0.1.3:12345"},
{kWireGuardPeerAllowedIPs, "fd00:0:0:0:1::/128,fd00:0:0:2::/64"}},
}));
}
TEST_F(WireGuardDriverTest, KeyPairGeneration) {
Error err;
const std::string kStorageId = "wireguard-test";
auto assert_pubkey_is = [&](const std::string& key) {
KeyValueStore provider;
ASSERT_TRUE(property_store_->GetKeyValueStoreProperty(kProviderProperty,
&provider, &err));
ASSERT_EQ(provider.Get<std::string>(kWireGuardPublicKey), key);
};
auto assert_pubkey_not_empty = [&]() {
KeyValueStore provider;
ASSERT_TRUE(property_store_->GetKeyValueStoreProperty(kProviderProperty,
&provider, &err));
ASSERT_FALSE(provider.Get<std::string>(kWireGuardPublicKey).empty());
};
driver_->Save(&fake_store_, kStorageId, /*save_credentials=*/true);
assert_pubkey_not_empty();
ResetDriver();
driver_->Load(&fake_store_, kStorageId);
assert_pubkey_not_empty();
property_store_->SetStringProperty(kWireGuardPrivateKey, kPrivateKey1, &err);
driver_->Save(&fake_store_, kStorageId, /*save_credentials=*/true);
assert_pubkey_is(kPrivateKey1);
property_store_->SetStringProperty(kWireGuardPrivateKey, "", &err);
driver_->Save(&fake_store_, kStorageId, /*save_credentials=*/true);
assert_pubkey_not_empty();
property_store_->SetStringProperty(kWireGuardPrivateKey, kPrivateKey2, &err);
driver_->Save(&fake_store_, kStorageId, /*save_credentials=*/true);
assert_pubkey_is(kPrivateKey2);
}
// Checks interface cleanup on timeout.
TEST_F(WireGuardDriverTest, OnConnectTimeout) {
InitializePropertyStore();
// Link is not created.
InvokeConnectAsyncKernel();
EXPECT_CALL(driver_event_handler_, OnDriverFailure(_, _));
driver_->OnConnectTimeout();
// Link is created by kernel.
InvokeConnectAsyncKernel();
InvokeLinkReady();
EXPECT_CALL(driver_event_handler_, OnDriverFailure(_, _));
EXPECT_CALL(*device_info(), DeleteInterface(kIfIndex));
driver_->OnConnectTimeout();
}
// Checks interface cleanup on disconnect before connected. Different with
// OnConnectTimeout, disconnect will not trigger an OnDriverFailure callback.
TEST_F(WireGuardDriverTest, DisconnectBeforeConnected) {
InitializePropertyStore();
// Link is not created.
InvokeConnectAsyncKernel();
driver_->Disconnect();
// Link is created by kernel.
InvokeConnectAsyncKernel();
InvokeLinkReady();
EXPECT_CALL(*device_info(), DeleteInterface(kIfIndex));
driver_->Disconnect();
}
TEST_F(WireGuardDriverTest, GetIPProperties) {
Error err;
auto create_kernel_link = [&]() {
InvokeConnectAsyncKernel();
InvokeLinkReady();
std::move(wireguard_tools_exit_callback_).Run(0);
};
auto assert_ip_address_is = [&](const Strings& value) {
KeyValueStore provider;
ASSERT_TRUE(property_store_->GetKeyValueStoreProperty(kProviderProperty,
&provider, &err));
ASSERT_EQ(provider.Get<Strings>(kWireGuardIPAddress), value);
};
// The case that the user configures only one IPv4 address.
InitializePropertyStore();
property_store_->SetStringsProperty(
kWireGuardIPAddress, std::vector<std::string>{kIPv4Address}, &err);
create_kernel_link();
assert_ip_address_is(std::vector<std::string>{kIPv4Address});
auto network_config = driver_->GetNetworkConfig();
ASSERT_NE(network_config, nullptr);
EXPECT_EQ(network_config->ipv4_address,
net_base::IPv4CIDR::CreateFromStringAndPrefix(kIPv4Address, 32));
EXPECT_TRUE(network_config->ipv6_addresses.empty());
driver_->Disconnect();
// The case that the user configures only one IPv6 address.
InitializePropertyStore();
property_store_->SetStringsProperty(
kWireGuardIPAddress, std::vector<std::string>{kIPv6Address1}, &err);
create_kernel_link();
assert_ip_address_is(std::vector<std::string>{kIPv6Address1});
network_config = driver_->GetNetworkConfig();
ASSERT_NE(network_config, nullptr);
ASSERT_FALSE(network_config->ipv4_address.has_value());
ASSERT_EQ(network_config->ipv6_addresses.size(), 1);
ASSERT_EQ(network_config->ipv6_addresses[0],
net_base::IPv6CIDR::CreateFromStringAndPrefix(kIPv6Address1, 128));
driver_->Disconnect();
// The case that the user configures one IPv4 address and one IPv6 address.
InitializePropertyStore();
// kWireguardIPAddress that contains one IPv4 address and one IPv6 address
// is set by default.
create_kernel_link();
assert_ip_address_is(std::vector<std::string>{kIPv4Address, kIPv6Address1});
network_config = driver_->GetNetworkConfig();
ASSERT_NE(network_config, nullptr);
EXPECT_EQ(network_config->ipv4_address,
net_base::IPv4CIDR::CreateFromStringAndPrefix(kIPv4Address, 32));
ASSERT_EQ(network_config->ipv6_addresses.size(), 1);
ASSERT_EQ(network_config->ipv6_addresses[0],
net_base::IPv6CIDR::CreateFromStringAndPrefix(kIPv6Address1, 128));
driver_->Disconnect();
// The case that the user configures two IPv6 addresses.
InitializePropertyStore();
property_store_->SetStringsProperty(
kWireGuardIPAddress,
std::vector<std::string>{kIPv6Address1, kIPv6Address2}, &err);
create_kernel_link();
assert_ip_address_is(std::vector<std::string>{kIPv6Address1, kIPv6Address2});
network_config = driver_->GetNetworkConfig();
ASSERT_NE(network_config, nullptr);
ASSERT_FALSE(network_config->ipv4_address.has_value());
ASSERT_EQ(network_config->ipv6_addresses.size(), 1);
ASSERT_EQ(network_config->ipv6_addresses[0],
net_base::IPv6CIDR::CreateFromStringAndPrefix(kIPv6Address1, 128));
driver_->Disconnect();
// The case that the user configures one wrong format address.
InitializePropertyStore();
property_store_->SetStringsProperty(
kWireGuardIPAddress, std::vector<std::string>{kWrongIPAddress}, &err);
EXPECT_CALL(driver_event_handler_, OnDriverFailure(_, _));
create_kernel_link();
assert_ip_address_is(std::vector<std::string>{kWrongIPAddress});
ASSERT_EQ(driver_->GetNetworkConfig(), nullptr);
driver_->Disconnect();
}
} // namespace
} // namespace shill