blob: ea1e952eca02410499b8a04988888c42cb0f3683 [file] [log] [blame]
// Copyright 2018 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/service.h"
#include <base/check.h>
#include <base/strings/string_number_conversions.h>
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
#include "shill/ipconfig.h"
#include "shill/mock_connection.h"
#include "shill/mock_control.h"
#include "shill/mock_device_info.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/network/mock_network.h"
#include "shill/network/network_config.h"
#include "shill/service_under_test.h"
#include "shill/store/fake_store.h"
#include "shill/store/property_store.h"
#include "shill/test_event_dispatcher.h"
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
using testing::StrictMock;
using testing::Test;
namespace shill {
namespace {
const char kAddress[] = "10.0.0.1";
const char kGateway[] = "10.0.0.254";
const int32_t kMtu = 512;
const char kNameServer0[] = "10.0.1.253";
const char kNameServer1[] = "10.0.1.252";
const char kNameServers[] = "10.0.1.253,10.0.1.252";
const char kSearchDomains[] = "example.com,chromium.org";
const char kSearchDomain0[] = "example.com";
const char kSearchDomain1[] = "chromium.org";
const int32_t kPrefixLen = 24;
const char kExcludedRoutes[] = "192.168.1.0/24,192.168.2.0/24";
const char kExcludedRoute0[] = "192.168.1.0/24";
const char kExcludedRoute1[] = "192.168.2.0/24";
const char kIncludedRoutes[] = "0.0.0.0/0,10.8.0.0/16";
const char kIncludedRoute0[] = "0.0.0.0/0";
const char kIncludedRoute1[] = "10.8.0.0/16";
} // namespace
// TODO(b/232177767): This test verifies the implementation inside Service,
// StaticIPParameters and Network. We should verify the logic in the first two
// classes against the interface of Network instead. Rework this test once the
// Network class is finalized.
class StaticIPParametersTest : public Test {
public:
StaticIPParametersTest() {
manager_ = std::make_unique<MockManager>(&control_interface_, &dispatcher_,
&metrics_);
device_info_ = std::make_unique<MockDeviceInfo>(manager_.get());
service_ = new ServiceUnderTest(manager_.get());
const std::string ifname = "eth1";
network_ = std::make_unique<Network>(
1, ifname, Technology::kEthernet, false, &network_handler_,
&control_interface_, device_info_.get(), &dispatcher_);
network_->set_connection_for_testing(
std::make_unique<MockConnection>(device_info_.get()));
network_->set_ipconfig(
std::make_unique<IPConfig>(&control_interface_, ifname));
// Call SetupConnection() explicitly to make this IPConfig object being
// selected.
network_->SetupConnection(network_->ipconfig());
}
~StaticIPParametersTest() {
service_ = nullptr;
network_ = nullptr;
device_info_ = nullptr;
manager_ = nullptr;
}
// Attaching the Network should trigger the event that Service pushes the
// static config into service, and since there is Connection and ipconfig() in
// the Network, the new IPConfig will be applied.
void AttachNetwork() {
service_->SetAttachedNetwork(network_->AsWeakPtr());
dispatcher_.task_environment().RunUntilIdle();
}
// Triggers that the Network applies the saved config it keeps, by a static
// config change without running the pending tasks.
void TriggerRestore() { service_->NotifyStaticIPConfigChanged(); }
void ExpectEmptyIPConfig() {
const auto& ipconfig_props = GetIPConfig()->properties();
EXPECT_TRUE(ipconfig_props.address.empty());
EXPECT_TRUE(ipconfig_props.gateway.empty());
EXPECT_EQ(IPConfig::kUndefinedMTU, ipconfig_props.mtu);
EXPECT_TRUE(ipconfig_props.dns_servers.empty());
EXPECT_TRUE(ipconfig_props.domain_search.empty());
EXPECT_FALSE(ipconfig_props.subnet_prefix);
EXPECT_TRUE(ipconfig_props.exclusion_list.empty());
EXPECT_TRUE(ipconfig_props.inclusion_list.empty());
EXPECT_TRUE(ipconfig_props.default_route);
}
// Modify an IP address string in some predictable way. There's no need
// for the output string to be valid from a networking perspective.
std::string VersionedAddress(const std::string& address, int version) {
std::string returned_address = address;
CHECK(returned_address.length());
returned_address[returned_address.length() - 1] += version;
return returned_address;
}
void ExpectPopulatedIPConfigWithVersion(int version) {
const auto& ipconfig_props = GetIPConfig()->properties();
EXPECT_EQ(VersionedAddress(kAddress, version), ipconfig_props.address);
EXPECT_EQ(VersionedAddress(kGateway, version), ipconfig_props.gateway);
EXPECT_EQ(kMtu + version, ipconfig_props.mtu);
EXPECT_EQ(2, ipconfig_props.dns_servers.size());
EXPECT_EQ(VersionedAddress(kNameServer0, version),
ipconfig_props.dns_servers[0]);
EXPECT_EQ(VersionedAddress(kNameServer1, version),
ipconfig_props.dns_servers[1]);
// VersionedAddress() increments the final character of each domain
// name.
EXPECT_EQ(2, ipconfig_props.domain_search.size());
EXPECT_EQ(VersionedAddress(kSearchDomain0, version),
ipconfig_props.domain_search[0]);
EXPECT_EQ(VersionedAddress(kSearchDomain1, version),
ipconfig_props.domain_search[1]);
EXPECT_EQ(kPrefixLen + version, ipconfig_props.subnet_prefix);
EXPECT_EQ(2, ipconfig_props.exclusion_list.size());
EXPECT_EQ(VersionedAddress(kExcludedRoute0, version),
ipconfig_props.exclusion_list[0]);
EXPECT_EQ(VersionedAddress(kExcludedRoute1, version),
ipconfig_props.exclusion_list[1]);
EXPECT_EQ(2, ipconfig_props.inclusion_list.size());
EXPECT_EQ(VersionedAddress(kIncludedRoute0, version),
ipconfig_props.inclusion_list[0]);
EXPECT_EQ(VersionedAddress(kIncludedRoute1, version),
ipconfig_props.inclusion_list[1]);
EXPECT_FALSE(ipconfig_props.default_route);
}
void ExpectPopulatedIPConfig() { ExpectPopulatedIPConfigWithVersion(0); }
void ExpectPropertiesWithVersion(const std::string& property_prefix,
int version) {
KeyValueStore args;
Error unused_error;
EXPECT_TRUE(service_->mutable_store()->GetKeyValueStoreProperty(
property_prefix + "Config", &args, &unused_error));
EXPECT_EQ(VersionedAddress(kAddress, version),
args.Get<std::string>(kAddressProperty));
EXPECT_EQ(VersionedAddress(kGateway, version),
args.Get<std::string>(kGatewayProperty));
EXPECT_EQ(kMtu + version, args.Get<int32_t>(kMtuProperty));
std::vector<std::string> kTestNameServers(
{VersionedAddress(kNameServer0, version),
VersionedAddress(kNameServer1, version)});
EXPECT_EQ(kTestNameServers, args.Get<Strings>(kNameServersProperty));
std::vector<std::string> kTestSearchDomains(
{VersionedAddress(kSearchDomain0, version),
VersionedAddress(kSearchDomain1, version)});
EXPECT_EQ(kTestSearchDomains, args.Get<Strings>(kSearchDomainsProperty));
EXPECT_EQ(kPrefixLen + version, args.Get<int32_t>(kPrefixlenProperty));
std::vector<std::string> kTestExcludedRoutes(
{VersionedAddress(kExcludedRoute0, version),
VersionedAddress(kExcludedRoute1, version)});
EXPECT_EQ(kTestExcludedRoutes, args.Get<Strings>(kExcludedRoutesProperty));
std::vector<std::string> kTestIncludedRoutes(
{VersionedAddress(kIncludedRoute0, version),
VersionedAddress(kIncludedRoute1, version)});
EXPECT_EQ(kTestIncludedRoutes, args.Get<Strings>(kIncludedRoutesProperty));
}
void ExpectProperties(const std::string& property_prefix) {
ExpectPropertiesWithVersion(property_prefix, 0);
}
void PopulateIPConfig() {
IPConfig::Properties ipconfig_props;
ipconfig_props.address = kAddress;
ipconfig_props.gateway = kGateway;
ipconfig_props.mtu = kMtu;
ipconfig_props.dns_servers = {kNameServer0, kNameServer1};
ipconfig_props.domain_search = {kSearchDomain0, kSearchDomain1};
ipconfig_props.subnet_prefix = kPrefixLen;
ipconfig_props.exclusion_list = {kExcludedRoute0, kExcludedRoute1};
ipconfig_props.inclusion_list = {kIncludedRoute0, kIncludedRoute1};
ipconfig_props.default_route = false;
GetIPConfig()->UpdateProperties(ipconfig_props);
}
void SetStaticProperties() { SetStaticPropertiesWithVersion(0); }
void SetStaticPropertiesWithVersion(int version) {
KeyValueStore args;
args.Set<std::string>(kAddressProperty,
VersionedAddress(kAddress, version));
args.Set<std::string>(kGatewayProperty,
VersionedAddress(kGateway, version));
args.Set<int32_t>(kMtuProperty, kMtu + version);
args.Set<Strings>(kNameServersProperty,
{VersionedAddress(kNameServer0, version),
VersionedAddress(kNameServer1, version)});
args.Set<Strings>(kSearchDomainsProperty,
{VersionedAddress(kSearchDomain0, version),
VersionedAddress(kSearchDomain1, version)});
args.Set<int32_t>(kPrefixlenProperty, kPrefixLen + version);
args.Set<Strings>(kExcludedRoutesProperty,
{VersionedAddress(kExcludedRoute0, version),
VersionedAddress(kExcludedRoute1, version)});
args.Set<Strings>(kIncludedRoutesProperty,
{VersionedAddress(kIncludedRoute0, version),
VersionedAddress(kIncludedRoute1, version)});
Error error;
service_->mutable_store()->SetKeyValueStoreProperty(kStaticIPConfigProperty,
args, &error);
}
void SetStaticPropertiesWithoutRoute(PropertyStore* store) {
KeyValueStore args;
args.Set<std::string>(kAddressProperty, kAddress);
args.Set<std::string>(kGatewayProperty, kGateway);
args.Set<int32_t>(kMtuProperty, kMtu);
Error error;
store->SetKeyValueStoreProperty(kStaticIPConfigProperty, args, &error);
}
KeyValueStore GetStaticArgs() {
KeyValueStore ret;
Error unused_err;
CHECK(service_->store().GetKeyValueStoreProperty(kStaticIPConfigProperty,
&ret, &unused_err));
return ret;
}
KeyValueStore GetSavedArgs() {
KeyValueStore ret;
Error unused_err;
CHECK(service_->store().GetKeyValueStoreProperty(kSavedIPConfigProperty,
&ret, &unused_err));
return ret;
}
IPConfig* GetIPConfig() {
auto* ipconfig = network_->GetCurrentIPConfig();
CHECK(ipconfig);
return ipconfig;
}
protected:
MockControl control_interface_;
EventDispatcherForTest dispatcher_;
MockMetrics metrics_;
std::unique_ptr<MockManager> manager_;
std::unique_ptr<MockDeviceInfo> device_info_;
MockNetworkEventHandler network_handler_;
scoped_refptr<ServiceUnderTest> service_;
std::unique_ptr<Network> network_;
};
TEST_F(StaticIPParametersTest, InitState) {
ExpectEmptyIPConfig();
AttachNetwork();
ExpectEmptyIPConfig();
}
TEST_F(StaticIPParametersTest, ApplyEmptyParameters) {
PopulateIPConfig();
AttachNetwork();
ExpectPopulatedIPConfig();
}
TEST_F(StaticIPParametersTest, DefaultRoute) {
SetStaticPropertiesWithoutRoute(service_->mutable_store());
AttachNetwork();
EXPECT_TRUE(GetIPConfig()->properties().default_route);
SetStaticProperties();
dispatcher_.task_environment().RunUntilIdle();
EXPECT_FALSE(GetIPConfig()->properties().default_route);
TriggerRestore();
EXPECT_TRUE(GetIPConfig()->properties().default_route);
}
TEST_F(StaticIPParametersTest, ControlInterface) {
Error unused_error;
int version = 0;
auto* store = service_->mutable_store();
SetStaticProperties();
EXPECT_TRUE(store->Contains("StaticIPConfig"));
auto current_args = GetStaticArgs();
current_args.Remove("Address");
current_args.Remove("Mtu");
store->SetKeyValueStoreProperty("StaticIPConfig", current_args,
&unused_error);
current_args = GetStaticArgs();
EXPECT_FALSE(current_args.Contains<std::string>("Address"));
EXPECT_FALSE(current_args.Contains<int32_t>("PrefixLen"));
EXPECT_EQ(kGateway, current_args.Get<std::string>("Gateway"));
EXPECT_FALSE(current_args.Contains<int32_t>("Mtu"));
std::vector<std::string> kTestNameServers(
{VersionedAddress(kNameServer0, version),
VersionedAddress(kNameServer1, version)});
EXPECT_EQ(kTestNameServers, current_args.Get<Strings>("NameServers"));
std::vector<std::string> kTestSearchDomains(
{VersionedAddress(kSearchDomain0, version),
VersionedAddress(kSearchDomain1, version)});
EXPECT_EQ(kTestSearchDomains, current_args.Get<Strings>("SearchDomains"));
std::vector<std::string> kTestExcludedRoutes(
{VersionedAddress(kExcludedRoute0, version),
VersionedAddress(kExcludedRoute1, version)});
EXPECT_EQ(kTestExcludedRoutes, current_args.Get<Strings>("ExcludedRoutes"));
std::vector<std::string> kTestIncludedRoutes(
{VersionedAddress(kIncludedRoute0, version),
VersionedAddress(kIncludedRoute1, version)});
EXPECT_EQ(kTestIncludedRoutes, current_args.Get<Strings>("IncludedRoutes"));
}
TEST_F(StaticIPParametersTest, Profile) {
FakeStore store;
const std::string& id = service_->GetStorageIdentifier();
store.SetString(id, "StaticIP.Address", kAddress);
store.SetString(id, "StaticIP.Gateway", kGateway);
store.SetInt(id, "StaticIP.Mtu", kMtu);
store.SetString(id, "StaticIP.NameServers", kNameServers);
store.SetString(id, "StaticIP.SearchDomains", kSearchDomains);
store.SetInt(id, "StaticIP.Prefixlen", kPrefixLen);
store.SetString(id, "StaticIP.ExcludedRoutes", kExcludedRoutes);
store.SetString(id, "StaticIP.IncludedRoutes", kIncludedRoutes);
service_->Load(&store);
AttachNetwork();
ExpectPopulatedIPConfig();
service_->Save(&store);
std::string address;
EXPECT_TRUE(store.GetString(id, "StaticIP.Address", &address));
EXPECT_EQ(address, kAddress);
std::string gateway;
EXPECT_TRUE(store.GetString(id, "StaticIP.Gateway", &gateway));
EXPECT_EQ(gateway, kGateway);
int mtu;
EXPECT_TRUE(store.GetInt(id, "StaticIP.Mtu", &mtu));
EXPECT_EQ(mtu, kMtu);
std::string nameservers;
EXPECT_TRUE(store.GetString(id, "StaticIP.NameServers", &nameservers));
EXPECT_EQ(nameservers, kNameServers);
std::string searchdomains;
EXPECT_TRUE(store.GetString(id, "StaticIP.SearchDomains", &searchdomains));
EXPECT_EQ(searchdomains, kSearchDomains);
int prefixlen;
EXPECT_TRUE(store.GetInt(id, "StaticIP.Prefixlen", &prefixlen));
EXPECT_EQ(prefixlen, kPrefixLen);
std::string excludedroutes;
EXPECT_TRUE(store.GetString(id, "StaticIP.ExcludedRoutes", &excludedroutes));
EXPECT_EQ(excludedroutes, kExcludedRoutes);
std::string includedroutes;
EXPECT_TRUE(store.GetString(id, "StaticIP.IncludedRoutes", &includedroutes));
EXPECT_EQ(includedroutes, kIncludedRoutes);
}
TEST_F(StaticIPParametersTest, SavedParameters) {
Error unused_error;
AttachNetwork();
PopulateIPConfig();
// Set the config property will cause Network push a task to configure the
// IPConfig using that.
SetStaticPropertiesWithVersion(1);
dispatcher_.task_environment().RunUntilIdle();
// The version 0 properties in IPConfig are now in SavedIP.* properties, while
// the version 1 StaticIP parameters are now in IPConfig.
ExpectPropertiesWithVersion("SavedIP", 0);
ExpectPopulatedIPConfigWithVersion(1);
// Clear all "StaticIP" parameters. Current IPConfig will be recovered from
// saved config, and the saved config should be cleared.
service_->mutable_store()->SetKeyValueStoreProperty(
"StaticIPConfig", KeyValueStore(), &unused_error);
dispatcher_.task_environment().RunUntilIdle();
EXPECT_TRUE(GetSavedArgs().IsEmpty());
ExpectPopulatedIPConfigWithVersion(0);
// Reset current IPConfig to version 0.
PopulateIPConfig();
// Set static config to version to, and the current IPConfig should also be
// updated to version 2, and the saved config should record the previous
// IPConfig value with version 0.
SetStaticPropertiesWithVersion(2);
dispatcher_.task_environment().RunUntilIdle();
ExpectPopulatedIPConfigWithVersion(2);
ExpectPropertiesWithVersion("SavedIP", 0);
// Static IP parameters should be unchanged.
ExpectPropertiesWithVersion("StaticIP", 2);
}
} // namespace shill