From 2ee0e646963bdf346932e855dc8adbbb2ecf7c08 Mon Sep 17 00:00:00 2001 From: Jonas Oreland Date: Wed, 25 Aug 2021 15:43:02 +0200 Subject: [PATCH] Add support for manually configuring subnets as VPN This patch adds support for manually setting subnets that should be handled as VPN, i.e be subject to VpnPreference, in case webrtc fails to auto-detect VPNs. Bug: webrtc:13097 Change-Id: I42514f0677a35cfe30ad053570fa9c2a5b4a856b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/230122 Commit-Queue: Jonas Oreland Reviewed-by: Harald Alvestrand Cr-Commit-Position: refs/heads/main@{#34852} --- api/peer_connection_interface.h | 4 +++ p2p/base/port_allocator.h | 4 +++ p2p/client/basic_port_allocator.cc | 5 +++ p2p/client/basic_port_allocator.h | 2 ++ pc/peer_connection.cc | 3 +- pc/peer_connection_factory.cc | 1 + rtc_base/network.cc | 38 +++++++++++++++++++++++ rtc_base/network.h | 29 ++++++++++++++++++ rtc_base/network_unittest.cc | 49 ++++++++++++++++++++++++++++++ 9 files changed, 134 insertions(+), 1 deletion(-) diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index a9fd5e3501..b1700ce818 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h @@ -661,6 +661,10 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // perfect, adherence to this preference cannot be guaranteed. VpnPreference vpn_preference = VpnPreference::kDefault; + // List of address/length subnets that should be treated like + // VPN (in case webrtc fails to auto detect them). + std::vector vpn_list; + // // Don't forget to update operator== if adding something. // diff --git a/p2p/base/port_allocator.h b/p2p/base/port_allocator.h index e9b1d1ef54..1f37e86c65 100644 --- a/p2p/base/port_allocator.h +++ b/p2p/base/port_allocator.h @@ -406,6 +406,10 @@ class RTC_EXPORT PortAllocator : public sigslot::has_slots<> { vpn_preference_ = preference; } + // Set list of that shall be categorized as VPN. + // Implemented by BasicPortAllocator. + virtual void SetVpnList(const std::vector& vpn_list) {} + std::unique_ptr CreateSession( const std::string& content_name, int component, diff --git a/p2p/client/basic_port_allocator.cc b/p2p/client/basic_port_allocator.cc index 0a1b996892..6284625bed 100644 --- a/p2p/client/basic_port_allocator.cc +++ b/p2p/client/basic_port_allocator.cc @@ -1228,6 +1228,11 @@ void BasicPortAllocatorSession::PrunePortsAndRemoveCandidates( } } +void BasicPortAllocator::SetVpnList( + const std::vector& vpn_list) { + network_manager_->set_vpn_list(vpn_list); +} + // AllocationSequence AllocationSequence::AllocationSequence( diff --git a/p2p/client/basic_port_allocator.h b/p2p/client/basic_port_allocator.h index c3858f71a3..c043cae6e4 100644 --- a/p2p/client/basic_port_allocator.h +++ b/p2p/client/basic_port_allocator.h @@ -74,6 +74,8 @@ class RTC_EXPORT BasicPortAllocator : public PortAllocator { return relay_port_factory_; } + void SetVpnList(const std::vector& vpn_list) override; + private: void OnIceRegathering(PortAllocatorSession* session, IceRegatheringReason reason); diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 5d10107858..ad83a8781a 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -337,6 +337,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( absl::optional report_usage_pattern_delay_ms; absl::optional stable_writable_connection_ping_interval_ms; webrtc::VpnPreference vpn_preference; + std::vector vpn_list; }; static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), "Did you add something to RTCConfiguration and forget to " @@ -399,7 +400,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( report_usage_pattern_delay_ms == o.report_usage_pattern_delay_ms && stable_writable_connection_ping_interval_ms == o.stable_writable_connection_ping_interval_ms && - vpn_preference == o.vpn_preference; + vpn_preference == o.vpn_preference && vpn_list == o.vpn_list; } bool PeerConnectionInterface::RTCConfiguration::operator!=( diff --git a/pc/peer_connection_factory.cc b/pc/peer_connection_factory.cc index f8393f6fb0..8a76c92451 100644 --- a/pc/peer_connection_factory.cc +++ b/pc/peer_connection_factory.cc @@ -231,6 +231,7 @@ PeerConnectionFactory::CreatePeerConnectionOrError( } dependencies.allocator->SetNetworkIgnoreMask(options().network_ignore_mask); + dependencies.allocator->SetVpnList(configuration.vpn_list); std::unique_ptr event_log = worker_thread()->Invoke>( diff --git a/rtc_base/network.cc b/rtc_base/network.cc index e1d39ebff5..b485077129 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -541,6 +541,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, continue; } // Convert to InterfaceAddress. + // TODO(webrtc:13114): Convert ConvertIfAddrs to use rtc::Netmask. if (!ifaddrs_converter->ConvertIfAddrsToIPAddress(cursor, &ip, &mask)) { continue; } @@ -576,8 +577,16 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, vpn_underlying_adapter_type = network_monitor_->GetVpnUnderlyingAdapterType(cursor->ifa_name); } + int prefix_length = CountIPMaskBits(mask); prefix = TruncateIP(ip, prefix_length); + + if (adapter_type != ADAPTER_TYPE_VPN && + IsConfiguredVpn(prefix, prefix_length)) { + vpn_underlying_adapter_type = adapter_type; + adapter_type = ADAPTER_TYPE_VPN; + } + std::string key = MakeNetworkKey(std::string(cursor->ifa_name), prefix, prefix_length); auto iter = current_networks.find(key); @@ -766,8 +775,15 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, adapter_type = ADAPTER_TYPE_UNKNOWN; break; } + auto vpn_underlying_adapter_type = ADAPTER_TYPE_UNKNOWN; + if (adapter_type != ADAPTER_TYPE_VPN && + IsConfiguredVpn(prefix, prefix_length)) { + vpn_underlying_adapter_type = adapter_type; + adapter_type = ADAPTER_TYPE_VPN; + } std::unique_ptr network(new Network( name, description, prefix, prefix_length, adapter_type)); + network->set_underlying_type_for_vpn(vpn_underlying_adapter_type); network->set_default_local_address_provider(this); network->set_mdns_responder_provider(this); network->set_scope_id(scope_id); @@ -1118,4 +1134,26 @@ std::string Network::ToString() const { return ss.Release(); } +void BasicNetworkManager::set_vpn_list(const std::vector& vpn) { + if (thread_ == nullptr) { + vpn_ = vpn; + } else { + thread_->Invoke(RTC_FROM_HERE, [this, vpn] { vpn_ = vpn; }); + } +} + +bool BasicNetworkManager::IsConfiguredVpn(IPAddress prefix, + int prefix_length) const { + RTC_DCHECK_RUN_ON(thread_); + for (const auto& vpn : vpn_) { + if (prefix_length >= vpn.prefix_length()) { + auto copy = TruncateIP(prefix, vpn.prefix_length()); + if (copy == vpn.address()) { + return true; + } + } + } + return false; +} + } // namespace rtc diff --git a/rtc_base/network.h b/rtc_base/network.h index 74b4aed2f9..94350e0e4d 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -79,6 +79,25 @@ class MdnsResponderProvider { virtual webrtc::MdnsResponderInterface* GetMdnsResponder() const = 0; }; +// Network/mask in CIDR representation. +class NetworkMask { + public: + NetworkMask(const IPAddress& addr, int prefix_length) + : address_(addr), prefix_length_(prefix_length) {} + + const IPAddress& address() const { return address_; } + int prefix_length() const { return prefix_length_; } + + bool operator==(const NetworkMask& o) const { + return address_ == o.address_ && prefix_length_ == o.prefix_length_; + } + + private: + IPAddress address_; + // Length of valid bits in address_ (for ipv4 valid range is 0-32) + int prefix_length_; +}; + // Generic network manager interface. It provides list of local // networks. // @@ -158,6 +177,8 @@ class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider, // MdnsResponderProvider interface. webrtc::MdnsResponderInterface* GetMdnsResponder() const override; + + virtual void set_vpn_list(const std::vector& vpn) {} }; // Base class for NetworkManager implementations. @@ -249,6 +270,12 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, network_ignore_list_ = list; } + // Set a list of manually configured VPN's. + void set_vpn_list(const std::vector& vpn) override; + + // Check if |prefix| is configured as VPN. + bool IsConfiguredVpn(IPAddress prefix, int prefix_length) const; + // Bind a socket to interface that ip address belong to. // Implementation look up interface name and calls // BindSocketToNetwork on NetworkMonitor. @@ -305,6 +332,8 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, RTC_GUARDED_BY(thread_); bool allow_mac_based_ipv6_ RTC_GUARDED_BY(thread_) = false; bool bind_using_ifname_ RTC_GUARDED_BY(thread_) = false; + + std::vector vpn_; }; // Represents a Unix-type network interface, with a name and single address. diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc index 325d1a1a5c..e7b9982cc6 100644 --- a/rtc_base/network_unittest.cc +++ b/rtc_base/network_unittest.cc @@ -44,6 +44,12 @@ namespace rtc { namespace { +IPAddress IPFromString(const std::string& str) { + IPAddress ip; + RTC_CHECK(IPFromString(str, &ip)); + return ip; +} + class FakeNetworkMonitor : public NetworkMonitorInterface { public: void Start() override { started_ = true; } @@ -1385,4 +1391,47 @@ TEST_F(NetworkTest, NetworkCostVpn_VpnMoreExpensive) { delete net2; } +TEST_F(NetworkTest, VpnList) { + { + BasicNetworkManager manager; + manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)}); + manager.StartUpdating(); + EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32)); + EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.12.1"), 24)); + EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 16)); + EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 24)); + EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.133.1.1"), 32)); + EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.133.0.0"), 16)); + EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 15)); + } + { + BasicNetworkManager manager; + manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 24)}); + manager.StartUpdating(); + EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32)); + EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.1"), 32)); + } +} + +#if defined(WEBRTC_POSIX) +// TODO(webrtc:13114): Implement the InstallIpv4Network for windows. +TEST_F(NetworkTest, VpnListOverrideAdapterType) { + BasicNetworkManager manager; + manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)}); + manager.StartUpdating(); + + char if_name[20] = "eth0"; + auto addr_list = + InstallIpv4Network(if_name, "192.168.1.23", "255.255.255.255", manager); + + BasicNetworkManager::NetworkList list; + manager.GetNetworks(&list); + ASSERT_EQ(1u, list.size()); + EXPECT_EQ(ADAPTER_TYPE_VPN, list[0]->type()); + EXPECT_EQ(ADAPTER_TYPE_ETHERNET, list[0]->underlying_type_for_vpn()); + ClearNetworks(manager); + ReleaseIfAddrs(addr_list); +} +#endif // defined(WEBRTC_POSIX) + } // namespace rtc