blob: 87fece54fbff52f4a5205d30c7821262f05a4853 [file] [log] [blame]
// 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.
#ifndef DNS_PROXY_PROXY_H_
#define DNS_PROXY_PROXY_H_
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <base/files/scoped_file.h>
#include <base/functional/callback.h>
#include <base/memory/weak_ptr.h>
#include <brillo/daemons/dbus_daemon.h>
#include <chromeos/net-base/ip_address.h>
#include <chromeos/net-base/ipv4_address.h>
#include <chromeos/net-base/ipv6_address.h>
#include <chromeos/net-base/rtnl_listener.h>
#include <chromeos/net-base/rtnl_message.h>
#include <chromeos/patchpanel/dbus/client.h>
#include <chromeos/patchpanel/message_dispatcher.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include <shill/dbus/client/client.h>
#include "dns-proxy/ipc.pb.h"
#include "dns-proxy/metrics.h"
#include "dns-proxy/resolver.h"
namespace dns_proxy {
// The process that runs the actual proxying code.
class Proxy : public brillo::DBusDaemon {
public:
enum class Type { kSystem, kDefault, kARC };
struct Options {
Type type;
// Required for ARC proxies as it specifies which physical interface
// should (always) be tracked. This field is ignored (but should be empty)
// for the system and default network proxies.
std::string ifname;
};
using Logger = base::RepeatingCallback<void(std::ostream& stream)>;
Proxy(const Options& opts, int32_t fd, bool root_ns_enabled);
// For testing.
Proxy(const Options& opts,
std::unique_ptr<patchpanel::Client> patchpanel,
std::unique_ptr<shill::Client> shill,
std::unique_ptr<patchpanel::MessageDispatcher<SubprocessMessage>>
msg_dispatcher,
bool root_ns_enabled);
Proxy(const Proxy&) = delete;
Proxy& operator=(const Proxy&) = delete;
~Proxy() = default;
static const char* TypeToString(Type t);
static std::optional<Type> StringToType(const std::string& s);
friend std::ostream& operator<<(std::ostream& stream, const Proxy& proxy);
protected:
int OnInit() override;
void OnShutdown(int*) override;
// Added for testing.
virtual std::unique_ptr<Resolver> NewResolver(base::TimeDelta timeout,
base::TimeDelta retry_delay,
int max_num_retries);
private:
static const uint8_t kMaxShillPropertyRetries = 10;
// Helper for parsing and applying shill's DNSProxyDOHProviders property.
class DoHConfig {
public:
DoHConfig() = default;
DoHConfig(const DoHConfig&) = delete;
DoHConfig& operator=(const DoHConfig&) = delete;
~DoHConfig() = default;
// Get the name servers the network of the proxy is tracking.
const std::vector<net_base::IPv4Address>& ipv4_nameservers();
const std::vector<net_base::IPv6Address>& ipv6_nameservers();
// Stores the resolver to configure whenever settings are updated.
void set_resolver(Resolver* resolver);
// |ipv4_nameservers| and |ipv6_nameservers| are the list of name servers
// for the network the proxy is tracking.
void set_nameservers(
const std::vector<net_base::IPv4Address>& ipv4_nameservers,
const std::vector<net_base::IPv6Address>& ipv6_nameservers);
// |settings| is the DoH providers property we get from shill. It keys, as
// applicable, secure DNS provider endpoints to standard DNS name servers.
void set_providers(const brillo::VariantDictionary& providers);
void clear();
void set_metrics(Metrics* metrics);
void set_logger(Logger logger);
friend std::ostream& operator<<(std::ostream& stream,
const DoHConfig& config) {
if (config.logger_) {
config.logger_.Run(stream);
}
return stream;
}
private:
void update();
Resolver* resolver_{nullptr};
Logger logger_;
std::vector<net_base::IPv4Address> ipv4_nameservers_;
std::vector<net_base::IPv6Address> ipv6_nameservers_;
// If non-empty, the secure providers to use for always-on DoH.
std::set<std::string> secure_providers_;
// If non-empty, the secure providers to use for DoH with fallback to
// plain-text nameservers.
std::set<std::string> secure_providers_with_fallback_;
// If non-empty, maps name server IP addresses to secure DNS provider URL
// for automatic upgrade.
std::map<net_base::IPAddress, std::string> auto_providers_;
Metrics* metrics_{nullptr};
};
void Setup();
void OnPatchpanelReady(bool success);
void OnPatchpanelReset(bool reset);
void InitShill();
void OnShillReady(bool success);
void OnShillReset(bool reset);
void ApplyDeviceUpdate();
// Stops DNS proxy from proxying DNS queries. This is run whenever the device
// is not yet online.
void Stop();
// Start and stop DNS redirection rules by querying patchpanel's API. This is
// necessary to route corresponding DNS traffic to the DNS proxy.
// |addr| values can be either IPv4 or IPv6 address.
// Calls from each DNS proxy types will result in a different rule:
// - System:
// Rules to exclude traffic that is not using the underlying name
// server (EXCLUDE_DESTINATION).
// - Default:
// If |ifname| is empty, rules to redirect user traffic to the proxy
// (USER).
// If |ifname| is not empty, rules to redirect guests that track default
// network to the proxy (DEFAULT).
// - ARC:
// Rules to redirect ARC traffic to the proxy (ARC).
void StartDnsRedirection(
const std::string& ifname,
const net_base::IPAddress& addr,
const std::vector<std::string>& nameservers = std::vector<std::string>());
void StopDnsRedirection(const std::string& ifname, sa_family_t sa_family);
// Triggered whenever the device attached to the default network changes.
// |device| can be null and indicates the default service is disconnected.
void OnDefaultDeviceChanged(const shill::Client::Device* const device);
void OnDeviceChanged(const shill::Client::Device* const device);
void MaybeCreateResolver();
// Update DNS proxy's name servers to the currently stored |device_|. If
// |device_| is a VPN without name server, fallback to the physical default
// device's name servers by first querying shill for it.
void UpdateNameServers();
// Update DoH providers. If proxy is the default proxy and VPN is connected,
// DoH is disabled. Force the provider to always be empty.
void OnDoHProvidersChanged(const brillo::Any& value);
// Update DoH excluded or included domains.
void OnDoHExcludedDomainsChanged(const brillo::Any& value);
void OnDoHIncludedDomainsChanged(const brillo::Any& value);
// Notified by patchpanel whenever a change occurs in one of its virtual
// network devices.
void OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent event,
const patchpanel::Client::VirtualDevice& device);
// Listen on |device| IPv4 and IPv6 address for both TCP and UDP to handle DNS
// queries from the guest tied to the device.
bool ListenOnVirtualDevice(const patchpanel::Client::VirtualDevice& device,
sa_family_t sa_family);
void StopListenOnVirtualDevice(
const patchpanel::Client::VirtualDevice& device, sa_family_t sa_family);
// Start and stop DNS redirection rules upon virtual device changed.
void StartGuestDnsRedirection(const patchpanel::Client::VirtualDevice& device,
sa_family_t sa_family);
void StopGuestDnsRedirection(const patchpanel::Client::VirtualDevice& device,
sa_family_t sa_family);
// Listen on |addr| with the identifier of |ifname|. Returns false only if it
// fails to listen on UDP. An empty string is used as the identifier when
// |ifname| is empty.
bool Listen(struct sockaddr* addr, std::string_view ifname = "");
// Helper func to send the proxy IP addresses to the controller.
// Only valid for the system proxy.
void SendIPAddressesToController(
const std::optional<net_base::IPv4Address>& ipv4_addr,
const std::optional<net_base::IPv6Address>& ipv6_addr);
void ClearIPAddressesInController();
void SendProxyMessage(const ProxyMessage& proxy_msg);
void OnControllerMessageFailure();
void OnControllerMessage(const SubprocessMessage& msg);
// Callback from RTNL listener, calls NetNSRTNLMessageHandler or
// RootNSRTNLMessageHandler.
void RTNLMessageHandler(const net_base::RTNLMessage& msg);
// Callback from RTNL listener when DNS proxy is running on ConnectNamespace
// network namespace. Processes IPv6 address changes for the lan interface
// inside the network namespace.
void NetNSRTNLMessageHandler(const net_base::RTNLMessage& msg);
// Callback from RTNL listener when DNS proxy is running on the root network
// namespace. Processes IPv6 link-local address changes for guests TAP or
// bridges interface.
void RootNSRTNLMessageHandler(const net_base::RTNLMessage& msg);
// Calls patchpanel ConnectNamespace API and sets the necessary states.
bool ConnectNamespace();
// Returns whether or not the virtual device |device| is expected to be
// handled by the current DNS proxy process.
// The DEFAULT proxy handles all non-ARC VMs and the ARC proxies handles their
// respective ARC virtual devices.
bool IsValidVirtualDevice(
const patchpanel::Client::VirtualDevice& device) const;
void LogName(std::ostream& stream) const;
// Return the property accessor, creating it if needed.
shill::Client::ManagerPropertyAccessor* shill_props();
// Wrapper of if_nametoindex for unit tests.
virtual int IfNameToIndex(const char* ifname);
friend class ProxyTest;
FRIEND_TEST(ProxyTest, NonSystemProxy_OnShutdownDoesNotCallShill);
FRIEND_TEST(ProxyTest, SystemProxy_SendIPAddressesToController);
FRIEND_TEST(ProxyTest,
SystemProxy_SendIPAddressesToControllerEmptyNameserver);
FRIEND_TEST(ProxyTest, SystemProxy_ClearIPAddressesInController);
FRIEND_TEST(ProxyTest, ShillInitializedWhenReady);
FRIEND_TEST(ProxyTest, SystemProxy_ConnectedNamedspace);
FRIEND_TEST(ProxyTest, DefaultProxy_ConnectedNamedspace);
FRIEND_TEST(ProxyTest, ArcProxy_ConnectedNamedspace);
FRIEND_TEST(ProxyTest, StateClearedIfDefaultServiceDrops);
FRIEND_TEST(ProxyTest, ArcProxy_IgnoredIfDefaultServiceDrops);
FRIEND_TEST(ProxyTest, StateClearedIfDefaultServiceIsNotOnline);
FRIEND_TEST(ProxyTest, NewResolverStartsListeningOnDefaultServiceComesOnline);
FRIEND_TEST(ProxyTest, NameServersUpdatedOnDefaultServiceComesOnline);
FRIEND_TEST(ProxyTest, SystemProxy_IgnoresVPN);
FRIEND_TEST(ProxyTest, SystemProxy_GetsPhysicalDeviceOnInitialVPN);
FRIEND_TEST(ProxyTest, DefaultProxy_UsesVPN);
FRIEND_TEST(ProxyTest, ArcProxy_NameServersUpdatedOnDeviceChangeEvent);
FRIEND_TEST(ProxyTest, SystemProxy_NameServersUpdatedOnDeviceChangeEvent);
FRIEND_TEST(ProxyTest, DeviceChangeEventIgnored);
FRIEND_TEST(ProxyTest, BasicDoHDisable);
FRIEND_TEST(ProxyTest, BasicDoHAlwaysOn);
FRIEND_TEST(ProxyTest, BasicDoHAutomatic);
FRIEND_TEST(ProxyTest, BasicDoHSecureWithFallback);
FRIEND_TEST(ProxyTest, RemovesDNSQueryParameterTemplate_AlwaysOn);
FRIEND_TEST(ProxyTest, RemovesDNSQueryParameterTemplate_Automatic);
FRIEND_TEST(ProxyTest, RemovesDNSQueryParameterTemplate_SecureWithFallback);
FRIEND_TEST(ProxyTest, NewResolverConfiguredWhenSet);
FRIEND_TEST(ProxyTest, DoHModeChangingFixedNameServers);
FRIEND_TEST(ProxyTest, MultipleDoHProvidersForAlwaysOnMode);
FRIEND_TEST(ProxyTest, MultipleDoHProvidersForAutomaticMode);
FRIEND_TEST(ProxyTest, MultipleDoHProvidersForSecureWithFallbackMode);
FRIEND_TEST(ProxyTest, DoHBadAlwaysOnConfigSetsAutomaticMode);
FRIEND_TEST(ProxyTest, DefaultProxy_DisableDoHProvidersOnVPN);
FRIEND_TEST(ProxyTest, SystemProxy_SetsDnsRedirectionRule);
FRIEND_TEST(ProxyTest, SystemProxy_SetDnsRedirectionRuleIPv6Added);
FRIEND_TEST(ProxyTest, SystemProxy_SetDnsRedirectionRuleIPv6Deleted);
FRIEND_TEST(ProxyTest,
DefaultProxy_SetDnsRedirectionRuleDeviceAlreadyStarted);
FRIEND_TEST(ProxyTest, DefaultProxy_SetDnsRedirectionRuleNewDeviceStarted);
FRIEND_TEST(ProxyTest, DefaultProxy_SetDnsRedirectionRuleGuest);
FRIEND_TEST(ProxyTest, DefaultProxy_NeverSetsDnsRedirectionRuleOtherGuest);
FRIEND_TEST(ProxyTest, DefaultProxy_SetDnsRedirectionRuleWithoutIPv6);
FRIEND_TEST(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Added);
FRIEND_TEST(ProxyTest, DefaultProxy_SetDnsRedirectionRuleIPv6Deleted);
FRIEND_TEST(ProxyTest, DefaultProxy_SetDnsRedirectionRuleUnrelatedIPv6Added);
FRIEND_TEST(ProxyTest, ArcProxy_SetDnsRedirectionRuleDeviceAlreadyStarted);
FRIEND_TEST(ProxyTest, ArcProxy_SetDnsRedirectionRuleNewDeviceStarted);
FRIEND_TEST(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherIfname);
FRIEND_TEST(ProxyTest, ArcProxy_NeverSetsDnsRedirectionRuleOtherGuest);
FRIEND_TEST(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Added);
FRIEND_TEST(ProxyTest, ArcProxy_SetDnsRedirectionRuleIPv6Deleted);
FRIEND_TEST(ProxyTest, ArcProxy_SetDnsRedirectionRuleUnrelatedIPv6Added);
FRIEND_TEST(ProxyTest, UpdateNameServers);
FRIEND_TEST(ProxyTest, SystemProxy_NeverListenForGuests);
FRIEND_TEST(ProxyTest, DefaultProxy_ListenForGuests);
FRIEND_TEST(ProxyTest, DefaultProxy_NeverListenForOtherGuests);
FRIEND_TEST(ProxyTest, ArcProxy_ListenForGuests);
FRIEND_TEST(ProxyTest, ArcProxy_NeverListenForOtherGuests);
FRIEND_TEST(ProxyTest, ArcProxy_NeverListenForOtherIfname);
FRIEND_TEST(ProxyTest, DomainDoHConfigsUpdate);
FRIEND_TEST(ProxyTest, DomainDoHConfigsUpdate_ProxyStopped);
FRIEND_TEST(ProxyTest, ArcProxy_SetInterface);
FRIEND_TEST(ProxyTest, DefaultProxy_SetInterface);
FRIEND_TEST(ProxyTest, SystemProxy_SetInterface);
const Options opts_;
std::unique_ptr<patchpanel::Client> patchpanel_;
std::unique_ptr<shill::Client> shill_;
std::unique_ptr<shill::Client::ManagerPropertyAccessor> shill_props_;
base::ScopedFD ns_fd_;
patchpanel::Client::ConnectedNamespace ns_;
// Whether or not DNS proxy process is initialized. This is set to true when
// patchpanel client is connected, the network namespace for the proxy is
// ready, and the listening IP address is ready.
bool initialized_ = false;
// The IPv4 and IPv6 addresses of address DNS proxy is listening on.
// When |root_ns_enabled_| is true, these contain the address allocated by
// patchpanel on the loopback interface for DNS proxy. The value only exists
// for the system and default instance of DNS proxy.
// When |root_ns_enabled_| is false, these contain the addresses of the
// interface inside the network namespace |ns_|.
std::optional<net_base::IPv4Address> ipv4_address_;
std::optional<net_base::IPv6Address> ipv6_address_;
// Map of interface index to its link-local IPv6 addresses. This map is used
// to listen on the link-local address of the guest (Crostini, ARC, etc.).
std::map<int, net_base::IPv6Address> link_local_addresses_;
std::unique_ptr<Resolver> resolver_;
DoHConfig doh_config_;
std::vector<std::string> doh_excluded_domains_;
std::vector<std::string> doh_included_domains_;
std::unique_ptr<shill::Client::Device> device_;
bool shill_ready_{false};
// Mapping of interface name and socket family pair to a lifeline file
// descriptor. These file descriptors control the lifetime of the DNS
// redirection rules created through the patchpanel's DBus API.
// For USER DnsRedirectionRequest, the interface name will be empty as it is
// not needed.
std::map<std::pair<std::string, sa_family_t>, base::ScopedFD> lifeline_fds_;
Metrics metrics_;
const Metrics::ProcessType metrics_proc_type_;
// Whether or not DNS proxy is running on the root namespace.
const bool root_ns_enabled_;
// Helper to:
// - Send system proxy's IP addresses to be the controller. This is
// necessary to write to /etc/resolv.conf.
// - Receive shutdown event message from the controller for graceful
// shutdown.
std::unique_ptr<patchpanel::MessageDispatcher<SubprocessMessage>>
msg_dispatcher_;
// Listens for RTMGRP_IPV6_IFADDR messages and invokes RTNLMessageHandler.
std::unique_ptr<net_base::RTNLListener> addr_listener_;
base::WeakPtrFactory<Proxy> weak_factory_{this};
};
std::ostream& operator<<(std::ostream& stream, Proxy::Type type);
std::ostream& operator<<(std::ostream& stream, Proxy::Options opt);
} // namespace dns_proxy
#endif // DNS_PROXY_PROXY_H_