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