blob: 789fae715a1703783b55fd36e4e83c9c9299d4d3 [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.
#ifndef SHILL_DEVICE_INFO_H_
#define SHILL_DEVICE_INFO_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <base/callback.h>
#include <base/cancelable_callback.h>
#include <base/files/file_path.h>
#include <base/memory/weak_ptr.h>
#include <base/optional.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include <patchpanel/proto_bindings/patchpanel_service.pb.h>
#include "shill/net/byte_string.h"
#include "shill/net/ip_address.h"
#include "shill/net/shill_time.h"
#include "shill/net/sockets.h"
#include "shill/refptr_types.h"
#include "shill/technology.h"
#if !defined(DISABLE_CELLULAR)
#include "shill/cellular/cellular.h"
#endif
namespace shill {
class EventDispatcher;
class Manager;
class Metrics;
class RoutingTable;
class RTNLHandler;
class RTNLListener;
class RTNLMessage;
class Sockets;
#if !defined(DISABLE_WIFI)
class NetlinkManager;
class Nl80211Message;
#endif // DISABLE_WIFI
#if !defined(DISABLE_CELLULAR)
class Modem;
#endif
class DeviceInfo {
public:
// Type of callback function triggered when an RTNL link add event occurs.
// First parameter is link name and second parameter interface index.
using LinkReadyCallback = base::OnceCallback<void(const std::string&, int)>;
explicit DeviceInfo(Manager* manager);
DeviceInfo(const DeviceInfo&) = delete;
DeviceInfo& operator=(const DeviceInfo&) = delete;
virtual ~DeviceInfo();
virtual void BlockDevice(const std::string& device_name);
virtual void AllowDevice(const std::string& device_name);
virtual bool IsDeviceBlocked(const std::string& device_name);
void Start();
void Stop();
std::vector<std::string> GetUninitializedTechnologies() const;
// Adds |device| to this DeviceInfo instance so that we can handle its link
// messages, and registers it with the manager.
virtual void RegisterDevice(const DeviceRefPtr& device);
// Deregister the Device instance (if any) from interested parties like
// Manager and Metrics, and remove the Info corresponding to this
// interface. No-op if there is no Info for this interface index.
void DeregisterDevice(int interface_index);
virtual DeviceRefPtr GetDevice(int interface_index) const;
#if !defined(DISABLE_CELLULAR)
// Returns a valid Celluar Device as follows:
// 1. If no device matching |interface_index| exists, a new device is created.
// 2. If a matching device exists and all key |modem| properties match
// (see implementation for details), the existing device is returned.
// 3. If a matching Device exists but key |modem| properties do not match,
// the existing device is deregistered and a new device is created.
// Once a device is found or created, CreateCapability is called for the
// device to create a CellularCapability for |modem|.
// NOTE: |mac_address| and modem->path are not required to match. If a
// matching device is found, the mac address and modem path will be updated.
virtual CellularRefPtr GetCellularDevice(int interface_index,
const std::string& mac_address,
Modem* modem);
#endif
virtual bool GetMacAddress(int interface_index, ByteString* address) const;
// Queries the kernel for a MAC address for |interface_index|. Returns an
// empty ByteString on failure.
virtual ByteString GetMacAddressFromKernel(int interface_index) const;
// Queries the kernel for the MAC address of |peer| on |interface_index|.
// Returns true and populates |mac_address| on success, otherwise returns
// false.
virtual bool GetMacAddressOfPeer(int interface_index,
const IPAddress& peer,
ByteString* mac_address) const;
virtual bool GetFlags(int interface_index, unsigned int* flags) const;
virtual bool GetByteCounts(int interface_index,
uint64_t* rx_bytes,
uint64_t* tx_bytes) const;
virtual std::vector<IPAddress> GetAddresses(int interface_index) const;
// Flush all addresses associated with |interface_index|.
virtual void FlushAddresses(int interface_index) const;
// Returns whether this interface does not have |this_address|
// but has another non-temporary address of the same family.
virtual bool HasOtherAddress(int interface_index,
const IPAddress& this_address) const;
// Get the IPv6 DNS server addresses for |interface_index|. This method
// returns true and sets |address_list| and |life_time_seconds| if the IPv6
// DNS server addresses exists. Otherwise, it returns false and leave
// |address_list| and |life_time_seconds| unmodified. |life_time_seconds|
// indicates the number of the seconds the DNS server is still valid for at
// the time of this function call. Value of 0 means the DNS server is not
// valid anymore, and value of 0xFFFFFFFF means the DNS server is valid
// forever.
virtual bool GetIPv6DnsServerAddresses(int interface_index,
std::vector<IPAddress>* address_list,
uint32_t* life_time_seconds);
virtual bool CreateTunnelInterface(LinkReadyCallback callback);
virtual int OpenTunnelInterface(const std::string& interface_name) const;
virtual bool DeleteInterface(int interface_index) const;
virtual void AddVirtualInterfaceReadyCallback(
const std::string& interface_name, LinkReadyCallback callback);
// Returns the interface index for |interface_name| or -1 if unknown.
virtual int GetIndex(const std::string& interface_name) const;
// Sets the system hostname to |hostname|.
virtual bool SetHostname(const std::string& hostname) const;
// Gets the real user ID of the given |user_name| and returns it via |uid|.
// Returns true on success.
virtual bool GetUserId(const std::string& user_name, uid_t* uid);
// Notifies this object that patchpanel::Client is ready in Manager. Registers
// neighbor connected events handler via manager_->patchpanel_client().
void OnPatchpanelClientReady();
Manager* manager() const { return manager_; }
private:
friend class DeviceInfoDelayedCreationTest;
friend class DeviceInfoMockedGetUserId;
friend class DeviceInfoTechnologyTest;
friend class DeviceInfoTest;
FRIEND_TEST(CellularTest, StartLinked);
FRIEND_TEST(DeviceInfoTest, DeviceRemovedEvent);
FRIEND_TEST(DeviceInfoTest, GetUninitializedTechnologies);
FRIEND_TEST(DeviceInfoTest, HasSubdir); // For HasSubdir.
FRIEND_TEST(DeviceInfoTest, IPv6AddressChanged); // For infos_.
FRIEND_TEST(DeviceInfoTest, StartStop);
FRIEND_TEST(DeviceInfoTest, IPv6DnsServerAddressesChanged); // For infos_.
FRIEND_TEST(DeviceInfoMockedGetUserId,
AddRemoveAllowedInterface); // For rtnl_handler_, routing_table_.
FRIEND_TEST(DeviceInfoTest, CreateDeviceTunnel); // For pending_links_.
struct AddressData {
AddressData() : address(IPAddress::kFamilyUnknown), flags(0), scope(0) {}
AddressData(const IPAddress& address_in,
unsigned char flags_in,
unsigned char scope_in)
: address(address_in), flags(flags_in), scope(scope_in) {}
IPAddress address;
unsigned char flags;
unsigned char scope;
};
struct Info {
Info();
DeviceRefPtr device;
std::string name;
ByteString mac_address;
std::vector<AddressData> ip_addresses;
std::vector<IPAddress> ipv6_dns_server_addresses;
uint32_t ipv6_dns_server_lifetime_seconds;
time_t ipv6_dns_server_received_time_seconds;
unsigned int flags;
uint64_t rx_bytes;
uint64_t tx_bytes;
// This flag indicates that a link add RTNL message has been received for
// this interface. This is used to behave differently for the first link add
// message received for this interface index; |device| is unsuitable because
// some interfaces may undergo delayed Device creation.
bool received_add_link;
Technology technology;
};
// Create a Device object for the interface named |linkname|, with a
// string-form MAC address |address|, whose kernel interface index
// is |interface_index| and detected technology is |technology|.
virtual DeviceRefPtr CreateDevice(const std::string& link_name,
const std::string& address,
int interface_index,
Technology technology);
// Return the ARP type (ARPHRD_* from <net/if_arp.h>) of interface
// |iface_name|.
int GetDeviceArpType(const std::string& iface_name);
// Return the FilePath for a given |path_name| in the device sysinfo for
// a specific interface |iface_name|.
base::FilePath GetDeviceInfoPath(const std::string& iface_name,
const std::string& path_name);
// Return the preferred globally scoped IPv6 address for |interface_index|.
// If no primary IPv6 address exists, return nullptr.
const IPAddress* GetPrimaryIPv6Address(int interface_index);
// Return the contents of the device info file |path_name| for interface
// |iface_name| in output parameter |contents_out|. Returns true if file
// read succeeded, false otherwise.
bool GetDeviceInfoContents(const std::string& iface_name,
const std::string& path_name,
std::string* contents_out);
// Return the filepath for the target of the device info symbolic link
// |path_name| for interface |iface_name| in output parameter |path_out|.
// Returns true if symbolic link read succeeded, false otherwise.
bool GetDeviceInfoSymbolicLink(const std::string& iface_name,
const std::string& path_name,
base::FilePath* path_out);
// Classify the device named |iface_name| with RTNL kind |kind|, and return
// an identifier indicating its type.
virtual Technology GetDeviceTechnology(
const std::string& iface_name, const base::Optional<std::string>& kind);
// Checks the device specified by |iface_name| to see if it's a modem device.
// This method assumes that |iface_name| has already been determined to be
// using the cdc_ether / cdc_ncm driver.
bool IsCdcEthernetModemDevice(const std::string& iface_name);
// Returns true if |base_dir| has a subdirectory named |subdir|.
// |subdir| can be an immediate subdirectory of |base_dir| or can be
// several levels deep.
static bool HasSubdir(const base::FilePath& base_dir,
const base::FilePath& subdir);
// Returns true and sets |link_name| to the interface name contained
// in |msg| if one is provided. Returns false otherwise.
bool GetLinkNameFromMessage(const RTNLMessage& msg, std::string* link_name);
// Returns true if |msg| pertains to a blocked device whose link name
// is now different from the name it was assigned before.
bool IsRenamedBlockedDevice(const RTNLMessage& msg);
void AddLinkMsgHandler(const RTNLMessage& msg);
void DelLinkMsgHandler(const RTNLMessage& msg);
void LinkMsgHandler(const RTNLMessage& msg);
void AddressMsgHandler(const RTNLMessage& msg);
void RdnssMsgHandler(const RTNLMessage& msg);
const Info* GetInfo(int interface_index) const;
void DelayDeviceCreation(int interface_index);
void DelayedDeviceCreationTask();
void RetrieveLinkStatistics(int interface_index, const RTNLMessage& msg);
void RequestLinkStatistics();
#if !defined(DISABLE_WIFI)
// Use nl80211 to get information on |interface_index|.
void GetWiFiInterfaceInfo(int interface_index);
void OnWiFiInterfaceInfoReceived(const Nl80211Message& message);
void RecordDarkResumeWakeReason(const std::string& wake_reason);
#endif // DISABLE_WIFI
// Returns whether a device with name |interface_name| is guest.
bool IsGuestDevice(const std::string& interface_name);
void OnNeighborReachabilityEvent(
const patchpanel::NeighborReachabilityEventSignal& signal);
void set_sockets_for_test(std::unique_ptr<Sockets> sockets) {
sockets_ = std::move(sockets);
}
Manager* manager_;
EventDispatcher* dispatcher_ = nullptr;
Metrics* metrics_ = nullptr;
std::map<int, Info> infos_; // Maps interface index to Info.
std::map<std::string, int> indices_; // Maps interface name to index.
std::unique_ptr<RTNLListener> link_listener_;
std::unique_ptr<RTNLListener> address_listener_;
std::unique_ptr<RTNLListener> rdnss_listener_;
std::set<std::string> blocked_list_;
base::FilePath device_info_root_;
// Keep track of devices that require a delayed call to CreateDevice().
base::CancelableClosure delayed_devices_callback_;
std::set<int> delayed_devices_;
// Maintain a callback for the periodic link statistics poll task.
base::CancelableClosure request_link_statistics_callback_;
// Maintain the list of callbacks awaiting link ready event.
// Used by VPNServices for tunnel (through calling CreateTunnel with
// callback) and ppp (through direct registering callback).
// Callback are one-time and will be removed once triggered.
// Keys of the map are names of the link concerned.
std::map<std::string, LinkReadyCallback> pending_links_;
// Cache copy of singleton pointers.
RoutingTable* routing_table_;
RTNLHandler* rtnl_handler_;
#if !defined(DISABLE_WIFI)
NetlinkManager* netlink_manager_;
#endif // DISABLE_WIFI
// A member of the class so that a mock can be injected for testing.
std::unique_ptr<Sockets> sockets_;
Time* time_;
base::WeakPtrFactory<DeviceInfo> weak_factory_{this};
};
} // namespace shill
#endif // SHILL_DEVICE_INFO_H_