blob: 7cdef91a08dea6c71f332a4a8edc72cc7d89d88f [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/static_ip_parameters.h"
#include <string>
#include <vector>
#include <base/logging.h>
#include <base/notreached.h>
#include <base/strings/strcat.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <chromeos/dbus/service_constants.h>
#include "shill/error.h"
#include "shill/logging.h"
#include "shill/net/ip_address.h"
#include "shill/network/network_config.h"
#include "shill/store/property_accessor.h"
#include "shill/store/property_store.h"
#include "shill/store/store_interface.h"
namespace shill {
namespace {
constexpr char kConfigKeyPrefix[] = "StaticIP.";
struct Property {
enum class Type {
kInt32,
kString,
// Properties of type "Strings" are stored as a comma-separated list in the
// control interface and in the profile, but are stored as a vector of
// strings in the IPConfig properties.
kStrings
};
const char* name;
Type type;
};
constexpr Property kProperties[] = {
{kAddressProperty, Property::Type::kString},
{kGatewayProperty, Property::Type::kString},
{kMtuProperty, Property::Type::kInt32},
{kNameServersProperty, Property::Type::kStrings},
{kSearchDomainsProperty, Property::Type::kStrings},
{kPrefixlenProperty, Property::Type::kInt32},
{kIncludedRoutesProperty, Property::Type::kStrings},
{kExcludedRoutesProperty, Property::Type::kStrings},
};
// Converts the StaticIPParameters from KeyValueStore to NetworkConfig.
// Errors are ignored if any value is not valid.
NetworkConfig KeyValuesToNetworkConfig(const KeyValueStore& kvs) {
NetworkConfig ret;
if (kvs.Contains<std::string>(kAddressProperty)) {
const int prefix = kvs.Lookup<int32_t>(kPrefixlenProperty, 0);
const std::string addr = kvs.Get<std::string>(kAddressProperty);
ret.ipv4_address_cidr =
base::StrCat({addr, "/", base::NumberToString(prefix)});
}
ret.ipv4_route.gateway = kvs.GetOptionalValue<std::string>(kGatewayProperty);
ret.ipv4_route.included_route_prefixes =
kvs.GetOptionalValue<Strings>(kIncludedRoutesProperty);
ret.ipv4_route.excluded_route_prefixes =
kvs.GetOptionalValue<Strings>(kExcludedRoutesProperty);
ret.mtu = kvs.GetOptionalValue<int32_t>(kMtuProperty);
ret.dns_servers = kvs.GetOptionalValue<Strings>(kNameServersProperty);
ret.dns_search_domains =
kvs.GetOptionalValue<Strings>(kSearchDomainsProperty);
// TODO(b/232177767): Currently this is only used by VPN. Check that if the
// Network class can make this decision by itself after finishing the
// refactor.
if (ret.ipv4_route.included_route_prefixes.has_value()) {
ret.ipv4_default_route = false;
}
return ret;
}
} // namespace
KeyValueStore StaticIPParameters::NetworkConfigToKeyValues(
const NetworkConfig& props) {
KeyValueStore kvs;
if (props.ipv4_address_cidr.has_value()) {
IPAddress addr(IPAddress::kFamilyIPv4);
if (addr.SetAddressAndPrefixFromString(props.ipv4_address_cidr.value())) {
kvs.Set<std::string>(kAddressProperty, addr.ToString());
kvs.Set<int32_t>(kPrefixlenProperty, addr.prefix());
} else {
LOG(ERROR) << "props does not have a valid IPv4 address in CIDR "
<< props.ipv4_address_cidr.value();
}
}
kvs.SetFromOptionalValue<std::string>(kGatewayProperty,
props.ipv4_route.gateway);
kvs.SetFromOptionalValue<int32_t>(kMtuProperty, props.mtu);
kvs.SetFromOptionalValue<Strings>(kNameServersProperty, props.dns_servers);
kvs.SetFromOptionalValue<Strings>(kSearchDomainsProperty,
props.dns_search_domains);
kvs.SetFromOptionalValue<Strings>(kIncludedRoutesProperty,
props.ipv4_route.included_route_prefixes);
kvs.SetFromOptionalValue<Strings>(kExcludedRoutesProperty,
props.ipv4_route.excluded_route_prefixes);
return kvs;
}
StaticIPParameters::StaticIPParameters() = default;
StaticIPParameters::~StaticIPParameters() = default;
void StaticIPParameters::PlumbPropertyStore(PropertyStore* store) {
// Register KeyValueStore for both static ip parameters.
store->RegisterDerivedKeyValueStore(
kStaticIPConfigProperty,
KeyValueStoreAccessor(
new CustomAccessor<StaticIPParameters, KeyValueStore>(
this, &StaticIPParameters::GetStaticIPConfig,
&StaticIPParameters::SetStaticIP)));
}
bool StaticIPParameters::Load(const StoreInterface* storage,
const std::string& storage_id) {
KeyValueStore args;
for (const auto& property : kProperties) {
const std::string name(std::string(kConfigKeyPrefix) + property.name);
switch (property.type) {
case Property::Type::kInt32: {
int32_t value;
if (storage->GetInt(storage_id, name, &value)) {
args.Set<int32_t>(property.name, value);
} else {
args.Remove(property.name);
}
} break;
case Property::Type::kString: {
std::string value;
if (storage->GetString(storage_id, name, &value)) {
args.Set<std::string>(property.name, value);
} else {
args.Remove(property.name);
}
} break;
case Property::Type::kStrings: {
// Name servers field is stored in storage as comma separated string.
// Keep it as is to be backward compatible.
std::string value;
if (storage->GetString(storage_id, name, &value)) {
std::vector<std::string> string_list = base::SplitString(
value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
args.Set<Strings>(property.name, string_list);
} else {
args.Remove(property.name);
}
} break;
default:
NOTIMPLEMENTED();
break;
}
}
return SetStaticIP(args, nullptr);
}
void StaticIPParameters::Save(StoreInterface* storage,
const std::string& storage_id) {
const auto args = NetworkConfigToKeyValues(config_);
for (const auto& property : kProperties) {
const std::string name(std::string(kConfigKeyPrefix) + property.name);
bool property_exists = false;
switch (property.type) {
case Property::Type::kInt32:
if (args.Contains<int32_t>(property.name)) {
property_exists = true;
storage->SetInt(storage_id, name, args.Get<int32_t>(property.name));
}
break;
case Property::Type::kString:
if (args.Contains<std::string>(property.name)) {
property_exists = true;
storage->SetString(storage_id, name,
args.Get<std::string>(property.name));
}
break;
case Property::Type::kStrings:
if (args.Contains<Strings>(property.name)) {
property_exists = true;
// Name servers field is stored in storage as comma separated string.
// Keep it as is to be backward compatible.
storage->SetString(
storage_id, name,
base::JoinString(args.Get<Strings>(property.name), ","));
}
break;
default:
NOTIMPLEMENTED();
break;
}
if (!property_exists) {
storage->DeleteKey(storage_id, name);
}
}
}
KeyValueStore StaticIPParameters::GetStaticIPConfig(Error* /*error*/) {
return NetworkConfigToKeyValues(config_);
}
bool StaticIPParameters::SetStaticIP(const KeyValueStore& value,
Error* /*error*/) {
const auto current_args = NetworkConfigToKeyValues(config_);
if (current_args == value) {
return false;
}
config_ = KeyValuesToNetworkConfig(value);
return true;
}
void StaticIPParameters::Reset() {
config_ = NetworkConfig();
}
} // namespace shill