| // 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. |
| |
| #ifndef SHILL_DEVICE_INFO_H_ |
| #define SHILL_DEVICE_INFO_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/cancelable_callback.h> |
| #include <base/files/file_path.h> |
| #include <base/functional/callback.h> |
| #include <base/memory/weak_ptr.h> |
| #include <chromeos/patchpanel/dbus/client.h> |
| #include <gtest/gtest_prod.h> // for FRIEND_TEST |
| #include <net-base/mac_address.h> |
| #include <net-base/netlink_manager.h> |
| #include <net-base/rtnl_handler.h> |
| #include <net-base/rtnl_listener.h> |
| #include <net-base/rtnl_message.h> |
| #include <net-base/socket.h> |
| |
| #include "shill/metrics.h" |
| #include "shill/mockable.h" |
| #include "shill/refptr_types.h" |
| #include "shill/technology.h" |
| |
| namespace shill { |
| |
| class EventDispatcher; |
| class Manager; |
| class Nl80211Message; |
| |
| 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(); |
| |
| void BlockDevice(const std::string& device_name); |
| 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; |
| |
| virtual std::optional<net_base::MacAddress> GetMacAddress( |
| int interface_index) const; |
| |
| // Queries the kernel for a MAC address for |interface_index|. |
| // Returns std::nullopt on failure. |
| std::optional<net_base::MacAddress> GetMacAddressFromKernel( |
| int interface_index) const; |
| |
| // Returns hardware (permanent) MAC address for |interface_index|. The |
| // address is returned either from cache (if it is available from netlink |
| // notifications or previous calls) or from query to the kernel. |
| // Returns std::nullopt on failure. |
| mockable std::optional<net_base::MacAddress> GetPermAddress( |
| int interface_index); |
| // Queries the kernel for a hardware (permanent) MAC address for |
| // |interface_index|. Returns std::nullopt on failure. |
| std::optional<net_base::MacAddress> GetPermAddressFromKernel( |
| int interface_index) const; |
| |
| // Query IDs that identify the adapter (e.g. PCI IDs). Returns |false| if |
| // there was an error. Even if there was an error (say, when probing the |
| // |subsystem_id|), the method will still set the IDs it managed to detect, |
| // even it could not probe successfully all the IDs. IDs that could not be |
| // probed are left untouched. |
| bool GetWiFiHardwareIds(int interface_index, |
| int* vendor_id, |
| int* product_id, |
| int* subsystem_id) const; |
| |
| 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 bool CreateTunnelInterface(LinkReadyCallback callback); |
| virtual int OpenTunnelInterface(const std::string& interface_name) const; |
| |
| // Creates a wireguard interface in the kernel with name |interface_name|. |
| // Returns true if we send the message to the kernel successfully. |
| // |link_ready_callback| will be invoked when the created link is ready, |
| // otherwise |failure_callback| will be invoked if the kernel rejects our |
| // request. |
| virtual bool CreateWireGuardInterface(const std::string& interface_name, |
| LinkReadyCallback link_ready_callback, |
| base::OnceClosure failure_callback); |
| |
| // Creates a XFRM interface in the kernel with name |interface_name|, index of |
| // the underlying interface |underlying_if_index| and the XFRM interface |
| // identifier |xfrm_if_id|. See the following link for more details about |
| // these two parameters: |
| // https://wiki.strongswan.org/projects/strongswan/wiki/RouteBasedVPN#XFRM-Interfaces-on-Linux. |
| // Returns true if we send the message to the kernel successfully. |
| // |link_ready_callback| will be invoked when the created link is ready, |
| // otherwise |failure_callback| will be invoked if the kernel rejects our |
| // request. |
| virtual bool CreateXFRMInterface(const std::string& interface_name, |
| int underlying_if_index, |
| int xfrm_if_id, |
| LinkReadyCallback link_ready_callback, |
| base::OnceClosure failure_callback); |
| |
| virtual VirtualDevice* CreatePPPDevice(Manager* manager, |
| const std::string& ifname, |
| int ifindex); |
| |
| 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; |
| |
| // 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) const; |
| |
| // 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(DeviceInfoTest, DeviceRemovedEvent); |
| FRIEND_TEST(DeviceInfoTest, GetUninitializedTechnologies); |
| FRIEND_TEST(DeviceInfoTest, HasSubdir); // For HasSubdir. |
| FRIEND_TEST(DeviceInfoTest, StartStop); |
| FRIEND_TEST(DeviceInfoMockedGetUserId, |
| AddRemoveAllowedInterface); // For rtnl_handler_, routing_table_. |
| FRIEND_TEST(DeviceInfoTest, CreateDeviceTunnel); // For pending_links_. |
| |
| struct Info { |
| Info(); |
| |
| DeviceRefPtr device; |
| std::string name; |
| // MAC addresses of the device. |mac_address| is the address currently used |
| // and |perm_address| is a hardware MAC. These two should be the same with |
| // the following exceptions: |
| // - when MAC randomization is turned on for a given device (e.g. WiFi) then |
| // |mac_address| will be changing, |
| // - virtual devices do not have |perm_address|, only |mac_address|. |
| std::optional<net_base::MacAddress> mac_address; |
| std::optional<net_base::MacAddress> perm_address; |
| 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 MAC |
| // address |mac_address|, whose kernel interface index is |interface_index| |
| // and detected technology is |technology|. |
| virtual DeviceRefPtr CreateDevice( |
| const std::string& link_name, |
| std::optional<net_base::MacAddress> mac_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) const; |
| // 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) const; |
| // 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) const; |
| |
| // 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) const; |
| // Return the path of the lower device, if any |
| bool GetLowerDeviceInfoPath(const std::string& iface_name, |
| base::FilePath* path_out) const; |
| // 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 std::optional<std::string>& kind) const; |
| // 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) const; |
| // Query the IDs that identify the WiFi adapter of integrated chipsets like |
| // the ones included in some Qualcomm SoCs. |
| // To be compatible with the IDs of PCIe adapters, we set the vendor ID to the |
| // value 0x0000 (Metrics::kWiFiIntegratedAdapterVendorId) that is not assigned |
| // by the PCI-SIG. Integrated adapters are identified by the tuple |
| // (Metrics::kWiFiIntegratedAdapterVendorId, product_id, subsystem_id). |
| // Returns true if an integrated adapter was detected, false otherwise. |
| bool GetIntegratedWiFiHardwareIds(const std::string& iface_name, |
| int* vendor_id, |
| int* product_id, |
| int* subsystem_id) const; |
| // 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 net_base::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 net_base::RTNLMessage& msg); |
| |
| void AddLinkMsgHandler(const net_base::RTNLMessage& msg); |
| void DelLinkMsgHandler(const net_base::RTNLMessage& msg); |
| void LinkMsgHandler(const net_base::RTNLMessage& msg); |
| |
| const Info* GetInfo(int interface_index) const; |
| void DelayDeviceCreation(int interface_index); |
| void DelayedDeviceCreationTask(); |
| void RetrieveLinkStatistics(int interface_index, |
| const net_base::RTNLMessage& msg); |
| void RequestLinkStatistics(); |
| |
| // 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); |
| |
| // Returns whether a device with name |interface_name| is guest. |
| bool IsGuestDevice(const std::string& interface_name) const; |
| |
| void OnNeighborReachabilityEvent( |
| const patchpanel::Client::NeighborReachabilityEvent& event); |
| |
| // Callback registered in CreateWireGuardInterface() and |
| // CreateXFRMInterface(). Invoked by net_base::RTNLHandler, to notify the |
| // acknowledgement from the kernel for the adding link request. |
| void OnCreateInterfaceResponse(const std::string& interface_name, |
| base::OnceClosure failure_callback, |
| int32_t error); |
| |
| void set_socket_factory_for_test( |
| std::unique_ptr<net_base::SocketFactory> factory) { |
| socket_factory_ = std::move(factory); |
| } |
| |
| 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<net_base::RTNLListener> link_listener_; |
| std::set<std::string> blocked_list_; |
| base::FilePath device_info_root_; |
| |
| // Keep track of devices that require a delayed call to CreateDevice(). |
| base::CancelableOnceClosure delayed_devices_callback_; |
| std::set<int> delayed_devices_; |
| |
| // Maintain a callback for the periodic link statistics poll task. |
| base::CancelableOnceClosure 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. |
| net_base::RTNLHandler* rtnl_handler_; |
| net_base::NetlinkManager* netlink_manager_; |
| |
| // Used to create net_base::Socket. Keep it as a class member so that a mock |
| // can be injected for testing. |
| std::unique_ptr<net_base::SocketFactory> socket_factory_ = |
| std::make_unique<net_base::SocketFactory>(); |
| |
| base::WeakPtrFactory<DeviceInfo> weak_factory_{this}; |
| }; |
| |
| } // namespace shill |
| |
| #endif // SHILL_DEVICE_INFO_H_ |