blob: 7f4415faa85afbdd9f8b26f3e9bd23f67c5d6a4d [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/static_ip_parameters.h"
#include <base/strings/string_number_conversions.h>
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
#include "shill/fake_store.h"
#include "shill/ipconfig.h"
#include "shill/mock_control.h"
#include "shill/mock_ipconfig.h"
#include "shill/property_store.h"
using std::string;
using std::vector;
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 char kPeerAddress[] = "10.0.0.2";
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";
const IPConfig::Route kIncludedRoute0("0.0.0.0", 0, "10.0.0.254");
} // namespace
class StaticIPParametersTest : public Test {
public:
StaticIPParametersTest() = default;
void ExpectEmptyIPConfig() {
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_TRUE(ipconfig_props_.peer_address.empty());
EXPECT_FALSE(ipconfig_props_.subnet_prefix);
EXPECT_TRUE(ipconfig_props_.exclusion_list.empty());
EXPECT_TRUE(ipconfig_props_.routes.empty());
}
// Modify an IP address string in some predictable way. There's no need
// for the output string to be valid from a networking perspective.
string VersionedAddress(const string& address, int version) {
string returned_address = address;
CHECK(returned_address.length());
returned_address[returned_address.length() - 1] += version;
return returned_address;
}
void ExpectPopulatedIPConfigWithVersion(int version) {
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(VersionedAddress(kPeerAddress, version),
ipconfig_props_.peer_address);
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]);
// VersionedAddress() increments the final digit of the prefix on
// the IncludedRoutes property, and the final digit of the IP address
// on the Gateway property.
EXPECT_EQ(1, ipconfig_props_.routes.size());
EXPECT_EQ(kIncludedRoute0.host, ipconfig_props_.routes[0].host);
EXPECT_EQ(kIncludedRoute0.prefix + version,
ipconfig_props_.routes[0].prefix);
EXPECT_EQ(VersionedAddress(kIncludedRoute0.gateway, version),
ipconfig_props_.routes[0].gateway);
}
void ExpectPopulatedIPConfig() { ExpectPopulatedIPConfigWithVersion(0); }
void ExpectPropertiesWithVersion(PropertyStore* store,
const string& property_prefix,
int version) {
KeyValueStore args;
Error unused_error;
EXPECT_TRUE(store->GetKeyValueStoreProperty(property_prefix + "Config",
&args, &unused_error));
EXPECT_EQ(VersionedAddress(kAddress, version),
args.Get<string>(kAddressProperty));
EXPECT_EQ(VersionedAddress(kGateway, version),
args.Get<string>(kGatewayProperty));
EXPECT_EQ(kMtu + version, args.Get<int32_t>(kMtuProperty));
vector<string> kTestNameServers({VersionedAddress(kNameServer0, version),
VersionedAddress(kNameServer1, version)});
EXPECT_EQ(kTestNameServers, args.Get<Strings>(kNameServersProperty));
vector<string> kTestSearchDomains(
{VersionedAddress(kSearchDomain0, version),
VersionedAddress(kSearchDomain1, version)});
EXPECT_EQ(kTestSearchDomains, args.Get<Strings>(kSearchDomainsProperty));
EXPECT_EQ(VersionedAddress(kPeerAddress, version),
args.Get<string>(kPeerAddressProperty));
EXPECT_EQ(kPrefixLen + version, args.Get<int32_t>(kPrefixlenProperty));
vector<string> kTestExcludedRoutes(
{VersionedAddress(kExcludedRoute0, version),
VersionedAddress(kExcludedRoute1, version)});
EXPECT_EQ(kTestExcludedRoutes, args.Get<Strings>(kExcludedRoutesProperty));
vector<string> kTestIncludedRoutes(
{VersionedAddress(kIncludedRoutes, version)});
EXPECT_EQ(kTestIncludedRoutes, args.Get<Strings>(kIncludedRoutesProperty));
}
void ExpectProperties(PropertyStore* store, const string& property_prefix) {
ExpectPropertiesWithVersion(store, property_prefix, 0);
}
void PopulateIPConfig() {
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_.peer_address = kPeerAddress;
ipconfig_props_.subnet_prefix = kPrefixLen;
ipconfig_props_.exclusion_list = {kExcludedRoute0, kExcludedRoute1};
ipconfig_props_.routes = {kIncludedRoute0};
}
void SetStaticProperties(PropertyStore* store) {
SetStaticPropertiesWithVersion(store, 0);
}
void SetStaticPropertiesWithVersion(PropertyStore* store, int version) {
KeyValueStore args;
args.Set<string>(kAddressProperty, VersionedAddress(kAddress, version));
args.Set<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<string>(kPeerAddressProperty,
VersionedAddress(kPeerAddress, version));
args.Set<int32_t>(kPrefixlenProperty, kPrefixLen + version);
args.Set<Strings>(kExcludedRoutesProperty,
{VersionedAddress(kExcludedRoute0, version),
VersionedAddress(kExcludedRoute1, version)});
args.Set<Strings>(kIncludedRoutesProperty,
{VersionedAddress(kIncludedRoutes, version)});
Error error;
store->SetKeyValueStoreProperty(kStaticIPConfigProperty, args, &error);
}
KeyValueStore* static_args() { return &static_params_.args_; }
KeyValueStore* saved_args() { return &static_params_.saved_args_; }
protected:
StaticIPParameters static_params_;
IPConfig::Properties ipconfig_props_;
};
TEST_F(StaticIPParametersTest, InitState) {
ExpectEmptyIPConfig();
// Applying an empty set of parameters on an empty set of properties should
// be a no-op.
static_params_.ApplyTo(&ipconfig_props_);
ExpectEmptyIPConfig();
}
TEST_F(StaticIPParametersTest, ApplyEmptyParameters) {
PopulateIPConfig();
static_params_.ApplyTo(&ipconfig_props_);
ExpectPopulatedIPConfig();
}
TEST_F(StaticIPParametersTest, ControlInterface) {
PropertyStore store;
int version = 0;
static_params_.PlumbPropertyStore(&store);
SetStaticProperties(&store);
static_params_.ApplyTo(&ipconfig_props_);
ExpectPopulatedIPConfig();
EXPECT_TRUE(static_params_.ContainsAddress());
EXPECT_TRUE(store.Contains("StaticIPConfig"));
static_args()->Remove("Address");
EXPECT_FALSE(static_params_.ContainsAddress());
static_args()->Remove("Mtu");
IPConfig::Properties props;
const string kTestAddress("test_address");
props.address = kTestAddress;
const int32_t kTestMtu = 256;
props.mtu = kTestMtu;
static_params_.ApplyTo(&props);
EXPECT_EQ(kTestAddress, props.address);
EXPECT_EQ(kTestMtu, props.mtu);
EXPECT_FALSE(static_args()->Contains<string>("Address"));
EXPECT_EQ(kGateway, static_args()->Get<string>("Gateway"));
EXPECT_FALSE(static_args()->Contains<int32_t>("Mtu"));
vector<string> kTestNameServers({VersionedAddress(kNameServer0, version),
VersionedAddress(kNameServer1, version)});
EXPECT_EQ(kTestNameServers, static_args()->Get<Strings>("NameServers"));
vector<string> kTestSearchDomains(
{VersionedAddress(kSearchDomain0, version),
VersionedAddress(kSearchDomain1, version)});
EXPECT_EQ(kTestSearchDomains, static_args()->Get<Strings>("SearchDomains"));
EXPECT_EQ(VersionedAddress(kPeerAddress, version),
static_args()->Get<string>("PeerAddress"));
EXPECT_EQ(kPrefixLen + version, static_args()->Get<int32_t>("Prefixlen"));
vector<string> kTestExcludedRoutes(
{VersionedAddress(kExcludedRoute0, version),
VersionedAddress(kExcludedRoute1, version)});
EXPECT_EQ(kTestExcludedRoutes, static_args()->Get<Strings>("ExcludedRoutes"));
vector<string> kTestIncludedRoutes(
{VersionedAddress(kIncludedRoutes, version)});
EXPECT_EQ(kTestIncludedRoutes, static_args()->Get<Strings>("IncludedRoutes"));
}
TEST_F(StaticIPParametersTest, Profile) {
FakeStore store;
const string kID = "storage_id";
store.SetString(kID, "StaticIP.Address", kAddress);
store.SetString(kID, "StaticIP.Gateway", kGateway);
store.SetInt(kID, "StaticIP.Mtu", kMtu);
store.SetString(kID, "StaticIP.NameServers", kNameServers);
store.SetString(kID, "StaticIP.SearchDomains", kSearchDomains);
store.SetString(kID, "StaticIP.PeerAddress", kPeerAddress);
store.SetInt(kID, "StaticIP.Prefixlen", kPrefixLen);
store.SetString(kID, "StaticIP.ExcludedRoutes", kExcludedRoutes);
store.SetString(kID, "StaticIP.IncludedRoutes", kIncludedRoutes);
static_params_.Load(&store, kID);
static_params_.ApplyTo(&ipconfig_props_);
ExpectPopulatedIPConfig();
static_params_.Save(&store, kID);
std::string address;
EXPECT_TRUE(store.GetString(kID, "StaticIP.Address", &address));
EXPECT_EQ(address, kAddress);
std::string gateway;
EXPECT_TRUE(store.GetString(kID, "StaticIP.Gateway", &gateway));
EXPECT_EQ(gateway, kGateway);
int mtu;
EXPECT_TRUE(store.GetInt(kID, "StaticIP.Mtu", &mtu));
EXPECT_EQ(mtu, kMtu);
std::string nameservers;
EXPECT_TRUE(store.GetString(kID, "StaticIP.NameServers", &nameservers));
EXPECT_EQ(nameservers, kNameServers);
std::string searchdomains;
EXPECT_TRUE(store.GetString(kID, "StaticIP.SearchDomains", &searchdomains));
EXPECT_EQ(searchdomains, kSearchDomains);
std::string peeraddress;
EXPECT_TRUE(store.GetString(kID, "StaticIP.PeerAddress", &peeraddress));
EXPECT_EQ(peeraddress, kPeerAddress);
int prefixlen;
EXPECT_TRUE(store.GetInt(kID, "StaticIP.Prefixlen", &prefixlen));
EXPECT_EQ(prefixlen, kPrefixLen);
std::string excludedroutes;
EXPECT_TRUE(store.GetString(kID, "StaticIP.ExcludedRoutes", &excludedroutes));
EXPECT_EQ(excludedroutes, kExcludedRoutes);
std::string includedroutes;
EXPECT_TRUE(store.GetString(kID, "StaticIP.IncludedRoutes", &includedroutes));
EXPECT_EQ(includedroutes, kIncludedRoutes);
}
TEST_F(StaticIPParametersTest, SavedParameters) {
// Calling RestoreTo() when no parameters are set should not crash or
// add any entries.
static_params_.RestoreTo(&ipconfig_props_);
ExpectEmptyIPConfig();
PopulateIPConfig();
PropertyStore static_params_props;
static_params_.PlumbPropertyStore(&static_params_props);
SetStaticPropertiesWithVersion(&static_params_props, 1);
static_params_.ApplyTo(&ipconfig_props_);
// The version 0 properties in |ipconfig_props_| are now in SavedIP.*
// properties, while the version 1 StaticIP parameters are now in
// |ipconfig_props_|.
ExpectPropertiesWithVersion(&static_params_props, "SavedIP", 0);
ExpectPopulatedIPConfigWithVersion(1);
// Clear all "StaticIP" parameters.
static_args()->Clear();
// Another ApplyTo() call rotates the version 1 properties in
// |ipconfig_props_| over to SavedIP.*. Since there are no StaticIP
// parameters, |ipconfig_props_| should remain populated with version 1
// parameters.
static_params_.ApplyTo(&ipconfig_props_);
ExpectPropertiesWithVersion(&static_params_props, "SavedIP", 1);
ExpectPopulatedIPConfigWithVersion(1);
// Reset |ipconfig_props_| to version 0.
PopulateIPConfig();
// A RestoreTo() call moves the version 1 "SavedIP" parameters into
// |ipconfig_props_|.
SetStaticPropertiesWithVersion(&static_params_props, 2);
static_params_.RestoreTo(&ipconfig_props_);
ExpectPopulatedIPConfigWithVersion(1);
// All "SavedIP" parameters should be cleared.
EXPECT_TRUE(saved_args()->IsEmpty());
// Static IP parameters should be unchanged.
ExpectPropertiesWithVersion(&static_params_props, "StaticIP", 2);
}
TEST_F(StaticIPParametersTest, IPConfigRefreshed) {
MockControl control;
scoped_refptr<MockIPConfig> ipconfig = new MockIPConfig(&control, "int0");
static_params_.SetIPConfig(ipconfig->weak_ptr_factory_.GetWeakPtr());
EXPECT_CALL(*ipconfig, Refresh());
PropertyStore store;
static_params_.PlumbPropertyStore(&store);
SetStaticProperties(&store);
}
} // namespace shill