blob: f0bcf941dc0ef34e92d378899831db918f35be08 [file] [log] [blame]
// Copyright 2016 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PATCHPANEL_SHILL_CLIENT_H_
#define PATCHPANEL_SHILL_CLIENT_H_
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <base/containers/flat_set.h>
#include <base/memory/weak_ptr.h>
#include <brillo/any.h>
#include <chromeos/net-base/ipv4_address.h>
#include <chromeos/net-base/ipv6_address.h>
#include <chromeos/net-base/network_config.h>
#include <chromeos/net-base/technology.h>
#include <dbus/bus.h>
#include <dbus/object_path.h>
#include "patchpanel/system.h"
// Provided by <shill/dbus-proxies.h>. Use forward declaration here since that
// header is expensive to compile, and thus we should avoid it being spread too
// widely.
namespace org::chromium::flimflam {
class ManagerProxy;
}
namespace patchpanel {
// Listens for shill signals over dbus in order to:
// - Find which network interface are currently managed by shill and to which
// shill Device they are associated.
// - Figure out which network interface (if any) is being used as the default
// logical service.
// - Invoke callbacks when the IPConfigs of a shill Device has changed.
class ShillClient {
public:
// Represents the properties of an object of org.chromium.flimflam.Device.
// Only contains the properties we care about.
struct Device {
// Interface name of the shill Device, corresponding to the
// kInterfaceProperty value. b/273741099: The kInterfaceProperty value must
// be tracked separately to ensure that patchpanel can advertise it in its
// virtual NetworkDevice messages in the |phys_ifname| field. This allows
// ARC and dns-proxy to join shill Device information with patchpanel
// virtual NetworkDevice information without knowing explicitly about
// Cellular multiplexed interfaces.
std::string shill_device_interface_property;
// Technology type of this Device.
std::optional<net_base::Technology> technology;
// Interface name of the primary multiplexed interface. Only defined for
// Cellular Devices. For Cellular Device not using multiplexing, this value
// is equivalent to the kInterfaceProperty value.
std::optional<std::string> primary_multiplexed_interface;
// Index of the network interface used for the packet datapath. This is
// always derived from the interface name by querying the kernel directly.
int ifindex;
// Name of the network interface associated with the shill Device and
// exposed in DBus as the shill Device kInterfaceProperty value. For a
// non-Cellular Device this is also the interface used for the packet
// datapath. For a Cellular Device, this corresponds to the interface
// associated with the modem.
std::string ifname;
// The DBus path of the shill Service currently selected by the shill
// Device, if any.
std::string service_path;
// An anonymous name that uniquely identifies the Service until reboot and
// does not contain PIIs.
std::string service_logname;
// IP configuration for this shill Device. For multiplexed Cellular Devices
// this corresponds to the IP configuration of the primary network
// interface.
net_base::NetworkConfig network_config;
// The session identifier of the shill Network session this shill Device is
// associated to.
std::optional<int> session_id;
// A string that can be used in logs and will be consistent with shill's
// Network::LoggingTag() output.
std::string logging_tag;
// Return if the device is connected by checking if IPv4 or IPv6 address is
// available.
bool IsConnected() const;
// Return if the device has no IPv4 address and has an IPv6 address.
bool IsIPv6Only() const;
// Returns the name of the network interface that is used for the packet
// datapath. For a non-Cellular Device this is equivalent to |ifname|, and
// for a Cellular Device this corresponds to the primary multiplexed
// interface.
std::string_view ActiveIfname() const;
// Returns as a string the shill session IDs for the shill Network
// associated with this shill Device.
std::string SessionIDString() const;
};
// Client callback for learning when shill default logical and physical
// network change. |new_device| can be null if there is no logical or physical
// network currently. |prev_device| can be null if there was no logical or
// physical network before.
using DefaultDeviceChangeHandler = base::RepeatingCallback<void(
const Device* new_device, const Device* prev_device)>;
// Client callback for learning which shill Devices were created or removed by
// shill.
using DevicesChangeHandler = base::RepeatingCallback<void(
const std::vector<Device>& added, const std::vector<Device>& removed)>;
// Client callback for listening to IPConfig changes on any shill Device with
// interface name |ifname|. Changes to the IP configuration of a VPN
// connection are not taken into account.
using IPConfigsChangeHandler =
base::RepeatingCallback<void(const Device& device)>;
// Client callback for listening to IPv6 network changes on any shill physical
// Device. The changes are identified by IPv6 prefix change. Note that any
// IPv6 prefix change also triggers all IPConfigsChangeHandler registered
// callbacks. Changes to the IPv6 network of a VPN connection are not taken
// into account.
using IPv6NetworkChangeHandler =
base::RepeatingCallback<void(const Device& device)>;
// The DNS-over-HTTPS service providers which are URLs of the secure DNS
// service endpoints. Different from the DNSProxyDOHProviders property in
// shill (see shill/doc/manager-api.doc), this struct does not contain the IPs
// of name servers since they are not used in patchpanel now.
using DoHProviders = base::flat_set<std::string>;
// Client callback for listening to DoH providers change events on the Manager
// object of shill.
using DoHProvidersChangeHandler = base::RepeatingCallback<void()>;
static std::unique_ptr<ShillClient> New(const scoped_refptr<dbus::Bus>& bus,
System* system);
ShillClient(const ShillClient&) = delete;
ShillClient& operator=(const ShillClient&) = delete;
virtual ~ShillClient();
// Registers the provided handler for changes in shill default logical or
// physical network.
// The handler will be called once immediately at registration
// with the current default logical or physical network as |new_device| and
// an empty Device as |prev_device|.
void RegisterDefaultLogicalDeviceChangedHandler(
const DefaultDeviceChangeHandler& handler);
void RegisterDefaultPhysicalDeviceChangedHandler(
const DefaultDeviceChangeHandler& handler);
void RegisterDevicesChangedHandler(const DevicesChangeHandler& handler);
void RegisterIPConfigsChangedHandler(const IPConfigsChangeHandler& handler);
void RegisterIPv6NetworkChangedHandler(
const IPv6NetworkChangeHandler& handler);
// Registers the provided handler for changes in DoH provider list. The
// handler will be called once immediately at registration.
void RegisterDoHProvidersChangedHandler(
const DoHProvidersChangeHandler& handler);
void ScanDevices();
// Updates the cache of NetworkConfig and shill session IDs for the shill
// Network associated with the interface index |ifindex|.
void UpdateNetworkConfigCache(int ifindex,
const net_base::NetworkConfig& network_config,
int session_id);
void ClearNetworkConfigCache(int ifindex);
// Finds the shill physical or VPN Device whose "Interface" property matches
// |shill_device_interface_property|. This function is meant for associating a
// shill Device to an interface name argument passed directly to patchpanel
// DBus RPCs for DownstreamNetwork and ConnectNamespace.
// TODO(b/273744897): Migrate callers to use the future Network primitive
// directly.
virtual const Device* GetDeviceByShillDeviceName(
const std::string& shill_device_interface_property) const;
// Finds the shill physical or VPN Device whose underlying data interface
// matches the interface index value |ifindex|. For Devices associated to
// Cellular multiplexed interfaces, this is the interface index value of the
// multiplexed interface.
virtual const Device* GetDeviceByIfindex(int ifindex) const;
// Returns the cached default logical shill Device, or nullptr if there is no
// default logical Device defined. Does not initiate a property fetch and does
// not block.
virtual const Device* default_logical_device() const;
// Returns the cached default physical shill Device, or nullptr if there is no
// default physical Device defined. Does not initiate a property fetch and
// does not block.
virtual const Device* default_physical_device() const;
// Returns interface names of all known shill physical Devices.
virtual const std::vector<Device> GetDevices() const;
// Returns the current DoH providers tracked in shill.
const DoHProviders& doh_providers() const { return doh_providers_; }
base::WeakPtr<ShillClient> AsWeakPtr() { return weak_factory_.GetWeakPtr(); }
protected:
ShillClient(const scoped_refptr<dbus::Bus>& bus, System* system);
// Called by New(). This is isolated from the constructor so that the
// ShillClient object used in unit test can avoid calling this. Ideally we
// should have an interface class as the base class to avoid having the real
// dependencies in the fake class.
void Initialize();
void OnManagerPropertyChangeRegistration(const std::string& interface,
const std::string& signal_name,
bool success);
void OnManagerPropertyChange(const std::string& property_name,
const brillo::Any& property_value);
void OnDevicePropertyChangeRegistration(
const std::string& dbus_interface_name,
const std::string& signal_name,
bool success);
void OnDevicePropertyChange(const dbus::ObjectPath& device_path,
const std::string& property_name,
const brillo::Any& property_value);
void OnDevicePrimaryMultiplexedInterfaceChange(
const dbus::ObjectPath& device_path,
const std::string& primary_multiplexed_interface);
void OnDeviceIPConfigChange(const dbus::ObjectPath& device_path);
virtual void OnDeviceNetworkConfigChange(int ifindex);
void NotifyIPConfigChangeHandlers(const Device& device);
void NotifyIPv6NetworkChangeHandlers(
const Device& device, const net_base::NetworkConfig& old_config);
// Fetches Device dbus properties via dbus for the shill Device identified
// by |device_path|. Returns std::nullopt if an error occurs. Note that this
// method will block the current thread.
virtual std::optional<Device> GetDeviceProperties(
const dbus::ObjectPath& device_path);
// Updates the current default logical and physical shill Devices for the
// system, and notifies listeners if there was any change.
void UpdateDefaultDevices();
// Returns the DBus paths of all shill Services. Can be overridden for
// testing.
virtual std::vector<dbus::ObjectPath> GetServices();
// Fetches shill Device DBus properties of the shill Device which has selected
// the shill Service with DBus path |service_path|. Returns std::nullopt if an
// error occurs or if the Service is not currently active. Note that this
// method will block the current thread. Can be overridden for testing.
virtual std::optional<Device> GetDeviceFromServicePath(
const dbus::ObjectPath& service_path);
// Gets the ObjectPath of the shill Device that is currently selecting the
// shill Service |service_path|, or returns std::nullopt if the Service is not
// active or not selected by any Device. Calling this function will also
// populate |service_logname_cache_| for |service_path|.
std::optional<dbus::ObjectPath> GetDevicePathFromServicePath(
const dbus::ObjectPath& service_path);
// Getter for FakeShillClient.
const std::map<int, net_base::NetworkConfig>& network_config_cache() const {
return network_config_cache_;
}
void set_doh_providers_for_testing(const DoHProviders& value);
private:
// Updates the list of currently known shill Devices, adding or removing
// Device tracking entries accordingly. Listeners that have registered a
// DevicesChangeHandler callback gets notified about any new or old Device
// change.
void UpdateDevices(const brillo::Any& property_value);
// Sets the internal shill Device variable tracking the system default logical
// network. Calls the registered client handlers if the default logical
// network changed. If a VPN is connected, the logical Device pertains to the
// VPN connection.
void SetDefaultLogicalDevice(const std::optional<Device>& device);
// Sets the internal shill Device variable tracking the system default
// physical network. Calls the registered client handlers if the default
// physical network changed.
void SetDefaultPhysicalDevice(const std::optional<Device>& device);
// Updates |doh_providers_| variable to track the DoH providers from shill.
// Also invokes the handlers if the list changes.
void UpdateDoHProviders(const brillo::Any& property_value);
// Tracks the system default physical network chosen by shill.
std::optional<Device> default_physical_device_;
// Tracks the system default logical network chosen by shill. This corresponds
// to the physical or VPN shill Device associated with the default logical
// network service.
std::optional<Device> default_logical_device_;
// Maps of all current shill physical Devices that are active, indexed by
// shill Device identifier. VPN Devices and inactive Devices are ignored.
std::map<dbus::ObjectPath, Device> devices_;
// Sets of shill Device Dbus object path for all the shill physical Devices
// seen so far. Unlike |devices_|, entries in this set will never be
// removed during the lifetime of this class. We maintain this set mainly for
// keeping track of the shill Device object proxies we have created, to avoid
// registering the handler on the same object twice.
std::set<dbus::ObjectPath> known_device_paths_;
// A map used for remembering the interface name and interface index of a
// shill Device after the underlying network interface has been removed, keyed
// by the shill Device's "Interface" property. This information is necessary
// when cleaning up the state of various subsystems in patchpanel that
// directly references the interface name or the interface index. This
// information can be missing when:
// - After receiving the interface removal event (RTM_DELLINK event or shill
// DBus event), the interface index cannot be retrieved anymore.
// - b/273741099: After the disconnection of the primary Network of a
// Cellular Device, the name of primary multiplexed interface is unknown.
std::map<std::string, std::pair<std::string, int>> datapath_interface_cache_;
// A map from interface index to ShillClient::IPconfig. This map is updated
// from the ConfigureNetwork D-Bus calls via UpdateNetworkConfigCache() and
// ClearNetworkConfigCache. This map tracks the NetworkConfigs on the network
// interfaces which patchpanel cares about (plus the the secondary
// multiplexed APN connection, we call ConfigureNetwork but shill_client does
// not track it). The NetworkConfig in the Device objects exposed by
// ShillClient will be updated and retrieved from this cache instead of some
// other D-Bus calls to shill.
std::map<int, net_base::NetworkConfig> network_config_cache_;
// A map of interface index to shill Network session id values. This mapping
// is updated for a given interface index when UpdateNetworkConfigCache() is
// called.
std::map<int, int> session_id_cache_;
// A map of Service DBus path to Service logging names. This mapping is stable
// until reboot and only serves to avoid looking Service properties when
// constructing shill Device objects. Entries from this cache are never
// removed and it will keep growing overtime in parallel to shill's own list
// of Services.
std::map<dbus::ObjectPath, std::string> service_logname_cache_;
// Tracks the DoH providers from the DNSProxyDOHProviders property on shill's
// Manager.
DoHProviders doh_providers_;
// Called when the shill Device used as the default logical network changes.
std::vector<DefaultDeviceChangeHandler> default_logical_device_handlers_;
// Called when the shill Device used as the default physical network changes.
std::vector<DefaultDeviceChangeHandler> default_physical_device_handlers_;
// Called when the list of network interfaces managed by shill changes.
std::vector<DevicesChangeHandler> device_handlers_;
// Called when the IPConfigs of any shill Device changes.
std::vector<IPConfigsChangeHandler> ipconfigs_handlers_;
// Called when the IPv6 network of any shill Device changes.
std::vector<IPv6NetworkChangeHandler> ipv6_network_handlers_;
// Called when the DNSProxyDOHProviders property changes.
std::vector<DoHProvidersChangeHandler> doh_provider_handlers_;
scoped_refptr<dbus::Bus> bus_;
std::unique_ptr<org::chromium::flimflam::ManagerProxy> manager_proxy_;
// Owned by Manager
System* system_;
base::WeakPtrFactory<ShillClient> weak_factory_{this};
};
std::ostream& operator<<(std::ostream& stream, const ShillClient::Device& dev);
std::ostream& operator<<(std::ostream& stream,
const std::optional<ShillClient::Device>& dev);
std::ostream& operator<<(std::ostream& stream, const ShillClient::Device* dev);
} // namespace patchpanel
#endif // PATCHPANEL_SHILL_CLIENT_H_