| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CHROME_BROWSER_ASH_CROSTINI_CROSTINI_PORT_FORWARDER_H_ |
| #define CHROME_BROWSER_ASH_CROSTINI_CROSTINI_PORT_FORWARDER_H_ |
| |
| #include <string> |
| #include <unordered_map> |
| |
| #include "base/files/scoped_file.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/values.h" |
| #include "chrome/browser/ash/crostini/crostini_util.h" |
| #include "components/keyed_service/core/keyed_service.h" |
| |
| class Profile; |
| |
| namespace crostini { |
| |
| extern const char kDefaultInterfaceToForward[]; |
| extern const char kWlanInterface[]; |
| extern const char kPortNumberKey[]; |
| extern const char kPortProtocolKey[]; |
| extern const char kPortInterfaceKey[]; |
| extern const char kPortLabelKey[]; |
| extern const char kPortVmNameKey[]; |
| extern const char kPortContainerNameKey[]; |
| |
| class CrostiniPortForwarder : public KeyedService { |
| public: |
| class Observer : public base::CheckedObserver { |
| public: |
| // Called when a port's active state changes. |
| virtual void OnActivePortsChanged(const base::Value::List& activePorts) = 0; |
| virtual void OnActiveNetworkChanged(const base::Value& interface, |
| const base::Value& ipAddress) = 0; |
| }; |
| |
| enum class Protocol { |
| TCP = 0, |
| UDP = 1, |
| }; |
| |
| struct PortRuleKey { |
| uint16_t port_number; |
| Protocol protocol_type; |
| guest_os::GuestId container_id; |
| |
| bool operator==(const PortRuleKey& other) const { |
| return port_number == other.port_number && |
| protocol_type == other.protocol_type; |
| } |
| }; |
| |
| // Helper for using PortRuleKey as key entries in std::unordered_maps. |
| struct PortRuleKeyHasher { |
| std::size_t operator()(const PortRuleKey& k) const { |
| return ((std::hash<uint16_t>()(k.port_number) ^ |
| (std::hash<Protocol>()(k.protocol_type) << 1)) >> |
| 1); |
| } |
| }; |
| |
| using ResultCallback = base::OnceCallback<void(bool)>; |
| |
| void AddObserver(Observer* observer) { observers_.AddObserver(observer); } |
| |
| void RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| // The result_callback will only be called with success=true IF all conditions |
| // pass. This means a port setting has been successfully updated in the |
| // iptables and the profile preference setting has also been successfully |
| // updated. |
| void ActivatePort(const guest_os::GuestId& container_id, |
| uint16_t port_number, |
| const Protocol& protocol_type, |
| ResultCallback result_callback); |
| void AddPort(const guest_os::GuestId& container_id, |
| uint16_t port_number, |
| const Protocol& protocol_type, |
| const std::string& label, |
| ResultCallback result_callback); |
| void DeactivatePort(const guest_os::GuestId& container_id, |
| uint16_t port_number, |
| const Protocol& protocol_type, |
| ResultCallback result_callback); |
| void RemovePort(const guest_os::GuestId& container_id, |
| uint16_t port_number, |
| const Protocol& protocol_type, |
| ResultCallback result_callback); |
| |
| // TODO(matterchen): For the two following methods, implement callback |
| // results. |
| |
| // Deactivate all ports belonging to the container_id and removes them from |
| // the preferences. |
| void RemoveAllPorts(const guest_os::GuestId& container_id); |
| |
| // Deactivate all active ports belonging to the container_id and set their |
| // preference to inactive such that these ports will not be automatically |
| // re-forwarded on re-startup. This is called on container shutdown. |
| void DeactivateAllActivePorts(const guest_os::GuestId& container_id); |
| |
| base::Value::List GetActivePorts(); |
| base::Value::List GetActiveNetworkInfo(); |
| |
| size_t GetNumberOfForwardedPortsForTesting(); |
| std::optional<base::Value> ReadPortPreferenceForTesting( |
| const PortRuleKey& key); |
| void ActiveNetworksChanged(const std::string& interface, |
| const std::string& ip_address); |
| |
| static CrostiniPortForwarder* GetForProfile(Profile* profile); |
| |
| static void EnsureFactoryBuilt(); |
| |
| explicit CrostiniPortForwarder(Profile* profile); |
| |
| CrostiniPortForwarder(const CrostiniPortForwarder&) = delete; |
| CrostiniPortForwarder& operator=(const CrostiniPortForwarder&) = delete; |
| |
| ~CrostiniPortForwarder() override; |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(CrostiniPortForwarderTest, |
| TryActivatePortPermissionBrokerClientFail); |
| FRIEND_TEST_ALL_PREFIXES(CrostiniPortForwarderTest, GetActivePortsForUI); |
| |
| void SignalActivePortsChanged(); |
| bool MatchPortRuleDict(const base::Value& dict, const PortRuleKey& key); |
| bool MatchPortRuleContainerId(const base::Value& dict, |
| const guest_os::GuestId& container_id); |
| void AddNewPortPreference(const PortRuleKey& key, const std::string& label); |
| bool RemovePortPreference(const PortRuleKey& key); |
| std::optional<base::Value> ReadPortPreference(const PortRuleKey& key); |
| |
| void OnActivatePortCompleted(ResultCallback result_callback, |
| PortRuleKey key, |
| bool success); |
| void OnRemoveOrDeactivatePortCompleted(ResultCallback result_callback, |
| PortRuleKey key, |
| bool success); |
| void TryDeactivatePort(const PortRuleKey& key, |
| const guest_os::GuestId& container_id, |
| base::OnceCallback<void(bool)> result_callback); |
| void TryActivatePort(const PortRuleKey& key, |
| const guest_os::GuestId& container_id, |
| base::OnceCallback<void(bool)> result_callback); |
| void UpdateActivePortInterfaces(); |
| |
| // For each port rule (protocol, port, interface), keep track of the fd which |
| // requested it so we can release it on removal / deactivate. |
| std::unordered_map<PortRuleKey, base::ScopedFD, PortRuleKeyHasher> |
| forwarded_ports_; |
| |
| // Current interface to forward ports on. |
| std::string current_interface_; |
| std::string ip_address_; |
| |
| base::ObserverList<Observer> observers_; |
| |
| raw_ptr<Profile> profile_; |
| |
| base::WeakPtrFactory<CrostiniPortForwarder> weak_ptr_factory_{this}; |
| |
| }; // class |
| |
| } // namespace crostini |
| |
| #endif // CHROME_BROWSER_ASH_CROSTINI_CROSTINI_PORT_FORWARDER_H_ |