blob: 8fededfcd3533c7ae73b568e5f9107ccb6d068f3 [file] [log] [blame] [edit]
// 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/network/dhcpv4_config.h"
#include <optional>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_split.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <chromeos/dbus/service_constants.h>
#include <net-base/http_url.h>
#include <net-base/ip_address.h>
#include <net-base/ipv4_address.h>
#include "shill/logging.h"
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kDHCP;
} // namespace Logging
bool operator==(const DHCPv4Config::Data&, const DHCPv4Config::Data&) = default;
// static
bool DHCPv4Config::ParseClasslessStaticRoutes(
const std::string& classless_routes,
net_base::NetworkConfig* network_config) {
if (classless_routes.empty()) {
// It is not an error for this string to be empty.
return true;
}
const auto route_strings = base::SplitString(
classless_routes, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (route_strings.size() % 2) {
LOG(ERROR) << "In " << __func__ << ": Size of route_strings array "
<< "is a non-even number: " << route_strings.size();
return false;
}
std::vector<std::pair<net_base::IPv4CIDR, net_base::IPv4Address>> routes;
auto route_iterator = route_strings.begin();
// Classless routes are a space-delimited array of
// "destination/prefix gateway" values. As such, we iterate twice
// for each pass of the loop below.
while (route_iterator != route_strings.end()) {
const auto& destination_as_string = *route_iterator;
route_iterator++;
const auto destination =
net_base::IPv4CIDR::CreateFromCIDRString(destination_as_string);
if (!destination) {
LOG(ERROR) << "In " << __func__ << ": Expected an IP address/prefix "
<< "but got an unparsable: " << destination_as_string;
return false;
}
CHECK(route_iterator != route_strings.end());
const auto& gateway_as_string = *route_iterator;
route_iterator++;
const auto gateway =
net_base::IPv4Address::CreateFromString(gateway_as_string);
if (!gateway) {
LOG(ERROR) << "In " << __func__ << ": Expected a router IP address "
<< "but got an unparsable: " << gateway_as_string;
return false;
}
if (destination->prefix_length() == 0 && !network_config->ipv4_gateway) {
// If a default route is provided in the classless parameters and
// we don't already have one, apply this as the default route.
SLOG(2) << "In " << __func__ << ": Setting default gateway to "
<< gateway_as_string;
network_config->ipv4_gateway = gateway;
} else {
routes.emplace_back(std::make_pair(*destination, *gateway));
SLOG(2) << "In " << __func__ << ": Adding route to to "
<< destination_as_string << " via " << gateway_as_string;
}
}
network_config->rfc3442_routes.swap(routes);
return true;
}
// static
bool DHCPv4Config::ParseConfiguration(const KeyValueStore& configuration,
net_base::NetworkConfig* network_config,
DHCPv4Config::Data* dhcp_data) {
SLOG(2) << __func__;
std::string classless_static_routes;
bool default_gateway_parse_error = false;
net_base::IPv4Address address;
uint8_t prefix_length = 0;
std::string domain_name;
bool has_error = false;
for (const auto& it : configuration.properties()) {
const auto& key = it.first;
const brillo::Any& value = it.second;
SLOG(2) << "Processing key: " << key;
if (key == kConfigurationKeyIPAddress) {
address = net_base::IPv4Address(value.Get<uint32_t>());
if (address.IsZero()) {
LOG(ERROR) << "Invalid IP address.";
has_error = true;
}
} else if (key == kConfigurationKeySubnetCIDR) {
prefix_length = value.Get<uint8_t>();
} else if (key == kConfigurationKeyBroadcastAddress) {
network_config->ipv4_broadcast =
net_base::IPv4Address(value.Get<uint32_t>());
if (network_config->ipv4_broadcast->IsZero()) {
LOG(ERROR) << "Ignoring invalid broadcast address.";
network_config->ipv4_broadcast = std::nullopt;
has_error = true;
}
} else if (key == kConfigurationKeyRouters) {
const auto& routers = value.Get<std::vector<uint32_t>>();
if (routers.empty()) {
LOG(ERROR) << "No routers provided.";
default_gateway_parse_error = true;
} else {
network_config->ipv4_gateway = net_base::IPv4Address(routers[0]);
if (network_config->ipv4_gateway->IsZero()) {
LOG(ERROR) << "Failed to parse router parameter provided.";
network_config->ipv4_gateway = std::nullopt;
default_gateway_parse_error = true;
}
}
} else if (key == kConfigurationKeyDNS) {
const auto& servers = value.Get<std::vector<uint32_t>>();
for (auto it = servers.begin(); it != servers.end(); ++it) {
auto server = net_base::IPAddress(net_base::IPv4Address(*it));
if (server.IsZero()) {
LOG(ERROR) << "Ignoring invalid DNS address.";
has_error = true;
continue;
}
network_config->dns_servers.push_back(std::move(server));
}
} else if (key == kConfigurationKeyDomainName) {
domain_name = value.Get<std::string>();
} else if (key == kConfigurationKeyDomainSearch) {
network_config->dns_search_domains =
value.Get<std::vector<std::string>>();
} else if (key == kConfigurationKeyMTU) {
int mtu = value.Get<uint16_t>();
if (mtu > net_base::NetworkConfig::kMinIPv4MTU) {
network_config->mtu = mtu;
}
} else if (key == kConfigurationKeyCaptivePortalUri) {
// RFC 8910 specifies that the protocol of the URI must be HTTPS.
const auto uri =
net_base::HttpUrl::CreateFromString(value.Get<std::string>());
if (!uri.has_value() ||
uri->protocol() != net_base::HttpUrl::Protocol::kHttps) {
LOG(ERROR) << "Ignoring invalid captive portal uri: "
<< value.Get<std::string>();
has_error = true;
continue;
}
network_config->captive_portal_uri = *uri;
} else if (key == kConfigurationKeyClasslessStaticRoutes) {
classless_static_routes = value.Get<std::string>();
} else if (key == kConfigurationKeyVendorEncapsulatedOptions) {
dhcp_data->vendor_encapsulated_options = value.Get<ByteArray>();
} else if (key == kConfigurationKeyWebProxyAutoDiscoveryUrl) {
dhcp_data->web_proxy_auto_discovery = value.Get<std::string>();
} else if (key == kConfigurationKeyLeaseTime) {
dhcp_data->lease_duration = base::Seconds(value.Get<uint32_t>());
} else {
SLOG(2) << "Key ignored.";
}
}
if (!address.IsZero()) {
if (prefix_length > 0) {
network_config->ipv4_address =
net_base::IPv4CIDR::CreateFromAddressAndPrefix(address,
prefix_length);
}
if (!network_config->ipv4_address) {
LOG(ERROR) << "Invalid prefix length " << prefix_length
<< ", ignoring address " << address;
has_error = true;
}
}
if (!domain_name.empty() && network_config->dns_search_domains.empty()) {
network_config->dns_search_domains.push_back(domain_name + ".");
}
ParseClasslessStaticRoutes(classless_static_routes, network_config);
return !has_error &&
(!default_gateway_parse_error || network_config->ipv4_gateway);
}
} // namespace shill