diff --git a/rtc_base/network.cc b/rtc_base/network.cc index 07c39ae5c1..1b0ba367e6 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -212,7 +212,8 @@ AdapterType GetAdapterTypeFromName(const char* network_name) { return ADAPTER_TYPE_ETHERNET; } - if (MatchTypeNameWithIndexPattern(network_name, "wlan")) { + if (MatchTypeNameWithIndexPattern(network_name, "wlan") || + MatchTypeNameWithIndexPattern(network_name, "v4-wlan")) { return ADAPTER_TYPE_WIFI; } @@ -478,15 +479,15 @@ Network* NetworkManagerBase::GetNetworkFromAddress( return nullptr; } -BasicNetworkManager::BasicNetworkManager() - : allow_mac_based_ipv6_( - webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")) {} +BasicNetworkManager::BasicNetworkManager() : BasicNetworkManager(nullptr) {} BasicNetworkManager::BasicNetworkManager( NetworkMonitorFactory* network_monitor_factory) : network_monitor_factory_(network_monitor_factory), allow_mac_based_ipv6_( - webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")) {} + webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")), + bind_using_ifname_( + webrtc::field_trial::IsEnabled("WebRTC-BindUsingInterfaceName")) {} BasicNetworkManager::~BasicNetworkManager() {} @@ -865,6 +866,15 @@ void BasicNetworkManager::StartNetworkMonitor() { network_monitor_->SignalNetworksChanged.connect( this, &BasicNetworkManager::OnNetworksChanged); } + + if (network_monitor_->SupportsBindSocketToNetwork()) { + // Set NetworkBinder on SocketServer so that + // PhysicalSocket::Bind will call + // BasicNetworkManager::BindSocketToNetwork(), (that will lookup interface + // name and then call network_monitor_->BindSocketToNetwork()). + thread_->socketserver()->set_network_binder(this); + } + network_monitor_->Start(); } @@ -873,6 +883,13 @@ void BasicNetworkManager::StopNetworkMonitor() { return; } network_monitor_->Stop(); + + if (network_monitor_->SupportsBindSocketToNetwork()) { + // Reset NetworkBinder on SocketServer. + if (thread_->socketserver()->network_binder() == this) { + thread_->socketserver()->set_network_binder(nullptr); + } + } } void BasicNetworkManager::OnMessage(Message* msg) { @@ -954,6 +971,20 @@ void BasicNetworkManager::DumpNetworks() { } } +NetworkBindingResult BasicNetworkManager::BindSocketToNetwork( + int socket_fd, + const IPAddress& address) { + RTC_DCHECK_RUN_ON(thread_); + std::string if_name; + if (bind_using_ifname_) { + Network* net = GetNetworkFromAddress(address); + if (net != nullptr) { + if_name = net->name(); + } + } + return network_monitor_->BindSocketToNetwork(socket_fd, address, if_name); +} + Network::Network(const std::string& name, const std::string& desc, const IPAddress& prefix, diff --git a/rtc_base/network.h b/rtc_base/network.h index d2e3bc22a7..8b6b6235fa 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -194,11 +194,11 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { void set_default_local_addresses(const IPAddress& ipv4, const IPAddress& ipv6); + Network* GetNetworkFromAddress(const rtc::IPAddress& ip) const; + private: friend class NetworkTest; - Network* GetNetworkFromAddress(const rtc::IPAddress& ip) const; - EnumerationPermission enumeration_permission_; NetworkList networks_; @@ -225,6 +225,7 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { // of networks using OS APIs. class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, public MessageHandlerAutoCleanup, + public NetworkBinderInterface, public sigslot::has_slots<> { public: BasicNetworkManager(); @@ -248,6 +249,15 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, network_ignore_list_ = list; } + // Bind a socket to interface that ip address belong to. + // Implementation look up interface name and calls + // BindSocketToNetwork on NetworkMonitor. + // The interface name is needed as e.g ipv4 over ipv6 addresses + // are not exposed using Android functions, but it is possible + // bind an ipv4 address to the interface. + NetworkBindingResult BindSocketToNetwork(int socket_fd, + const IPAddress& address) override; + protected: #if defined(WEBRTC_POSIX) // Separated from CreateNetworks for tests. @@ -293,7 +303,8 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, nullptr; std::unique_ptr network_monitor_ RTC_GUARDED_BY(thread_); - bool allow_mac_based_ipv6_ = false; + bool allow_mac_based_ipv6_ RTC_GUARDED_BY(thread_) = false; + bool bind_using_ifname_ RTC_GUARDED_BY(thread_) = false; }; // Represents a Unix-type network interface, with a name and single address. diff --git a/rtc_base/network_monitor.h b/rtc_base/network_monitor.h index 4a3002f427..dddc2f60f4 100644 --- a/rtc_base/network_monitor.h +++ b/rtc_base/network_monitor.h @@ -36,6 +36,8 @@ enum class NetworkPreference { const char* NetworkPreferenceToString(NetworkPreference preference); +// This interface is set onto a socket server, +// where only the ip address is known at the time of binding. class NetworkBinderInterface { public: // Binds a socket to the network that is attached to |address| so that all @@ -83,6 +85,19 @@ class NetworkMonitorInterface { virtual NetworkPreference GetNetworkPreference( const std::string& interface_name) = 0; + // Does |this| NetworkMonitorInterface implement BindSocketToNetwork? + // Only Android returns true. + virtual bool SupportsBindSocketToNetwork() const { return false; } + + // Bind a socket to an interface specified by ip address and/or interface + // name. Only implemented on Android. + virtual NetworkBindingResult BindSocketToNetwork( + int socket_fd, + const IPAddress& address, + const std::string& interface_name) { + return NetworkBindingResult::NOT_IMPLEMENTED; + } + // Is this interface available to use? WebRTC shouldn't attempt to use it if // this returns false. // diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc index 73ddd81ce8..abad4796fe 100644 --- a/rtc_base/network_unittest.cc +++ b/rtc_base/network_unittest.cc @@ -76,9 +76,35 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { unavailable_adapters_ = unavailable_adapters; } + bool SupportsBindSocketToNetwork() const override { return true; } + + NetworkBindingResult BindSocketToNetwork( + int socket_fd, + const IPAddress& address, + const std::string& if_name) override { + if (absl::c_count(addresses_, address) > 0) { + return NetworkBindingResult::SUCCESS; + } + + for (auto const& iter : adapters_) { + if (if_name.find(iter) != std::string::npos) { + return NetworkBindingResult::SUCCESS; + } + } + return NetworkBindingResult::ADDRESS_NOT_FOUND; + } + + void set_ip_addresses(std::vector addresses) { + addresses_ = addresses; + } + + void set_adapters(std::vector adapters) { adapters_ = adapters; } + private: bool started_ = false; + std::vector adapters_; std::vector unavailable_adapters_; + std::vector addresses_; }; class FakeNetworkMonitorFactory : public NetworkMonitorFactory { @@ -1279,4 +1305,48 @@ TEST_F(NetworkTest, WebRTC_AllowMACBasedIPv6Address) { } #endif +#if defined(WEBRTC_POSIX) +TEST_F(NetworkTest, WebRTC_BindUsingInterfaceName) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-BindUsingInterfaceName/Enabled/"); + + char if_name1[20] = "wlan0"; + char if_name2[20] = "v4-wlan0"; + ifaddrs* list = nullptr; + list = AddIpv6Address(list, if_name1, "1000:2000:3000:4000:0:0:0:1", + "FFFF:FFFF:FFFF:FFFF::", 0); + list = AddIpv4Address(list, if_name2, "192.168.0.2", "255.255.255.255"); + NetworkManager::NetworkList result; + + // Sanity check that both interfaces are included by default. + FakeNetworkMonitorFactory factory; + BasicNetworkManager manager(&factory); + manager.StartUpdating(); + CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result); + EXPECT_EQ(2u, result.size()); + ReleaseIfAddrs(list); + bool changed; + // This ensures we release the objects created in CallConvertIfAddrs. + MergeNetworkList(manager, result, &changed); + result.clear(); + + FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager); + + IPAddress ipv6; + EXPECT_TRUE(IPFromString("1000:2000:3000:4000:0:0:0:1", &ipv6)); + IPAddress ipv4; + EXPECT_TRUE(IPFromString("192.168.0.2", &ipv4)); + + // The network monitor only knwos about the ipv6 address, interface. + network_monitor->set_adapters({"wlan0"}); + network_monitor->set_ip_addresses({ipv6}); + EXPECT_EQ(manager.BindSocketToNetwork(/* fd */ 77, ipv6), + NetworkBindingResult::SUCCESS); + + // But it will bind anyway using string matching... + EXPECT_EQ(manager.BindSocketToNetwork(/* fd */ 77, ipv4), + NetworkBindingResult::SUCCESS); +} +#endif + } // namespace rtc diff --git a/sdk/android/native_unittests/android_network_monitor_unittest.cc b/sdk/android/native_unittests/android_network_monitor_unittest.cc index e7a2d7d7db..20e756ae7b 100644 --- a/sdk/android/native_unittests/android_network_monitor_unittest.cc +++ b/sdk/android/native_unittests/android_network_monitor_unittest.cc @@ -74,7 +74,7 @@ TEST_F(AndroidNetworkMonitorTest, TestFindNetworkHandleUsingIpv4Address) { network_monitor_->SetNetworkInfos(net_infos); auto network_handle = - network_monitor_->FindNetworkHandleFromAddress(ipv4_address); + network_monitor_->FindNetworkHandleFromAddressOrName(ipv4_address, ""); ASSERT_TRUE(network_handle.has_value()); EXPECT_EQ(ipv4_handle, *network_handle); @@ -91,9 +91,9 @@ TEST_F(AndroidNetworkMonitorTest, TestFindNetworkHandleUsingFullIpv6Address) { network_monitor_->SetNetworkInfos(net_infos); auto network_handle1 = - network_monitor_->FindNetworkHandleFromAddress(ipv6_address1); + network_monitor_->FindNetworkHandleFromAddressOrName(ipv6_address1, ""); auto network_handle2 = - network_monitor_->FindNetworkHandleFromAddress(ipv6_address2); + network_monitor_->FindNetworkHandleFromAddressOrName(ipv6_address2, ""); ASSERT_TRUE(network_handle1.has_value()); EXPECT_EQ(ipv6_handle, *network_handle1); @@ -116,9 +116,9 @@ TEST_F(AndroidNetworkMonitorTest, network_monitor_->SetNetworkInfos(net_infos); auto network_handle1 = - network_monitor_->FindNetworkHandleFromAddress(ipv6_address1); + network_monitor_->FindNetworkHandleFromAddressOrName(ipv6_address1, ""); auto network_handle2 = - network_monitor_->FindNetworkHandleFromAddress(ipv6_address2); + network_monitor_->FindNetworkHandleFromAddressOrName(ipv6_address2, ""); ASSERT_TRUE(network_handle1.has_value()); EXPECT_EQ(ipv6_handle, *network_handle1); @@ -126,5 +126,47 @@ TEST_F(AndroidNetworkMonitorTest, EXPECT_EQ(ipv6_handle, *network_handle2); } +TEST_F(AndroidNetworkMonitorTest, TestFindNetworkHandleUsingIfName) { + ScopedFieldTrials field_trials("WebRTC-BindUsingInterfaceName/Enabled/"); + // Start() updates the states introduced by the field trial. + network_monitor_->Start(); + jni::NetworkHandle ipv6_handle = 200; + rtc::IPAddress ipv6_address1 = GetIpAddressFromIpv6String(kTestIpv6Address1); + + // Set up an IPv6 network. + jni::NetworkInformation net_info = + CreateNetworkInformation("wlan0", ipv6_handle, ipv6_address1); + std::vector net_infos(1, net_info); + network_monitor_->SetNetworkInfos(net_infos); + + rtc::IPAddress ipv4_address(kTestIpv4Address); + + // Search using ip address only... + auto network_handle1 = + network_monitor_->FindNetworkHandleFromAddressOrName(ipv4_address, ""); + + // Search using ip address AND if_name (for typical ipv4 over ipv6 tunnel). + auto network_handle2 = network_monitor_->FindNetworkHandleFromAddressOrName( + ipv4_address, "v4-wlan0"); + + ASSERT_FALSE(network_handle1.has_value()); + ASSERT_TRUE(network_handle2.has_value()); + EXPECT_EQ(ipv6_handle, *network_handle2); +} + +TEST_F(AndroidNetworkMonitorTest, TestUnderlyingVpnType) { + ScopedFieldTrials field_trials("WebRTC-BindUsingInterfaceName/Enabled/"); + jni::NetworkHandle ipv4_handle = 100; + rtc::IPAddress ipv4_address(kTestIpv4Address); + jni::NetworkInformation net_info = + CreateNetworkInformation("wlan0", ipv4_handle, ipv4_address); + net_info.type = jni::NETWORK_VPN; + net_info.underlying_type_for_vpn = jni::NETWORK_WIFI; + network_monitor_->SetNetworkInfos({net_info}); + + EXPECT_EQ(rtc::ADAPTER_TYPE_WIFI, + network_monitor_->GetVpnUnderlyingAdapterType("v4-wlan0")); +} + } // namespace test } // namespace webrtc diff --git a/sdk/android/src/jni/android_network_monitor.cc b/sdk/android/src/jni/android_network_monitor.cc index cb6bce2e05..03675132d7 100644 --- a/sdk/android/src/jni/android_network_monitor.cc +++ b/sdk/android/src/jni/android_network_monitor.cc @@ -247,11 +247,8 @@ void AndroidNetworkMonitor::Start() { find_network_handle_without_ipv6_temporary_part_ = webrtc::field_trial::IsEnabled( "WebRTC-FindNetworkHandleWithoutIpv6TemporaryPart"); - - // This is kind of magic behavior, but doing this allows the SocketServer to - // use this as a NetworkBinder to bind sockets on a particular network when - // it creates sockets. - network_thread_->socketserver()->set_network_binder(this); + bind_using_ifname_ = + webrtc::field_trial::IsEnabled("WebRTC-BindUsingInterfaceName"); // Needed for restart after Stop(). safety_flag_->SetAlive(); @@ -285,7 +282,8 @@ void AndroidNetworkMonitor::Stop() { // https://cs.chromium.org/chromium/src/net/udp/udp_socket_posix.cc rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork( int socket_fd, - const rtc::IPAddress& address) { + const rtc::IPAddress& address, + const std::string& if_name) { RTC_DCHECK_RUN_ON(network_thread_); // Android prior to Lollipop didn't have support for binding sockets to @@ -303,12 +301,18 @@ rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork( } absl::optional network_handle = - FindNetworkHandleFromAddress(address); + FindNetworkHandleFromAddressOrName(address, if_name); if (!network_handle) { + RTC_LOG(LS_WARNING) + << "BindSocketToNetwork unable to find network handle for" + << " addr: " << address.ToSensitiveString() << " ifname: " << if_name; return rtc::NetworkBindingResult::ADDRESS_NOT_FOUND; } if (*network_handle == 0 /* NETWORK_UNSPECIFIED */) { + RTC_LOG(LS_WARNING) << "BindSocketToNetwork 0 network handle for" + << " addr: " << address.ToSensitiveString() + << " ifname: " << if_name; return rtc::NetworkBindingResult::NOT_IMPLEMENTED; } @@ -375,11 +379,19 @@ rtc::NetworkBindingResult AndroidNetworkMonitor::BindSocketToNetwork( // ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back // the less descriptive ERR_FAILED. if (rv == 0) { + RTC_LOG(LS_VERBOSE) << "BindSocketToNetwork bound network handle for" + << " addr: " << address.ToSensitiveString() + << " ifname: " << if_name; return rtc::NetworkBindingResult::SUCCESS; } + + RTC_LOG(LS_WARNING) << "BindSocketToNetwork got error: " << rv + << " addr: " << address.ToSensitiveString() + << " ifname: " << if_name; if (rv == ENONET) { return rtc::NetworkBindingResult::NETWORK_CHANGED; } + return rtc::NetworkBindingResult::FAILURE; } @@ -402,8 +414,9 @@ void AndroidNetworkMonitor::OnNetworkConnected_n( } absl::optional -AndroidNetworkMonitor::FindNetworkHandleFromAddress( - const rtc::IPAddress& ip_address) const { +AndroidNetworkMonitor::FindNetworkHandleFromAddressOrName( + const rtc::IPAddress& ip_address, + const std::string& if_name) const { RTC_DCHECK_RUN_ON(network_thread_); RTC_LOG(LS_INFO) << "Find network handle."; if (find_network_handle_without_ipv6_temporary_part_) { @@ -417,14 +430,31 @@ AndroidNetworkMonitor::FindNetworkHandleFromAddress( return absl::make_optional(iter.first); } } - return absl::nullopt; } else { auto iter = network_handle_by_address_.find(ip_address); - if (iter == network_handle_by_address_.end()) { - return absl::nullopt; + if (iter != network_handle_by_address_.end()) { + return absl::make_optional(iter->second); } - return absl::make_optional(iter->second); } + + return FindNetworkHandleFromIfname(if_name); +} + +absl::optional +AndroidNetworkMonitor::FindNetworkHandleFromIfname( + const std::string& if_name) const { + RTC_DCHECK_RUN_ON(network_thread_); + if (bind_using_ifname_) { + for (auto const& iter : network_info_by_handle_) { + if (if_name.find(iter.second.interface_name) != std::string::npos) { + // Use partial match so that e.g if_name="v4-wlan0" is matched + // agains iter.first="wlan0" + return absl::make_optional(iter.first); + } + } + } + + return absl::nullopt; } void AndroidNetworkMonitor::OnNetworkDisconnected_n(NetworkHandle handle) { @@ -470,6 +500,18 @@ rtc::AdapterType AndroidNetworkMonitor::GetAdapterType( rtc::AdapterType type = (iter == adapter_type_by_name_.end()) ? rtc::ADAPTER_TYPE_UNKNOWN : iter->second; + + if (type == rtc::ADAPTER_TYPE_UNKNOWN && bind_using_ifname_) { + for (auto const& iter : adapter_type_by_name_) { + // Use partial match so that e.g if_name="v4-wlan0" is matched + // agains iter.first="wlan0" + if (if_name.find(iter.first) != std::string::npos) { + type = iter.second; + break; + } + } + } + if (type == rtc::ADAPTER_TYPE_UNKNOWN) { RTC_LOG(LS_WARNING) << "Get an unknown type for the interface " << if_name; } @@ -483,6 +525,17 @@ rtc::AdapterType AndroidNetworkMonitor::GetVpnUnderlyingAdapterType( rtc::AdapterType type = (iter == vpn_underlying_adapter_type_by_name_.end()) ? rtc::ADAPTER_TYPE_UNKNOWN : iter->second; + if (type == rtc::ADAPTER_TYPE_UNKNOWN && bind_using_ifname_) { + // Use partial match so that e.g if_name="v4-wlan0" is matched + // agains iter.first="wlan0" + for (auto const& iter : vpn_underlying_adapter_type_by_name_) { + if (if_name.find(iter.first) != std::string::npos) { + type = iter.second; + break; + } + } + } + return type; } diff --git a/sdk/android/src/jni/android_network_monitor.h b/sdk/android/src/jni/android_network_monitor.h index f6edf1b20f..6349a11cf6 100644 --- a/sdk/android/src/jni/android_network_monitor.h +++ b/sdk/android/src/jni/android_network_monitor.h @@ -64,8 +64,7 @@ struct NetworkInformation { std::string ToString() const; }; -class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface, - public rtc::NetworkBinderInterface { +class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface { public: AndroidNetworkMonitor(JNIEnv* env, const JavaRef& j_application_context); @@ -77,9 +76,14 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface, void Start() override; void Stop() override; + // Does |this| NetworkMonitorInterface implement BindSocketToNetwork? + // Only Android returns true. + virtual bool SupportsBindSocketToNetwork() const override { return true; } + rtc::NetworkBindingResult BindSocketToNetwork( int socket_fd, - const rtc::IPAddress& address) override; + const rtc::IPAddress& address, + const std::string& if_name) override; rtc::AdapterType GetAdapterType(const std::string& if_name) override; rtc::AdapterType GetVpnUnderlyingAdapterType( const std::string& if_name) override; @@ -106,8 +110,9 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface, jint preference); // Visible for testing. - absl::optional FindNetworkHandleFromAddress( - const rtc::IPAddress& address) const; + absl::optional FindNetworkHandleFromAddressOrName( + const rtc::IPAddress& address, + const std::string& ifname) const; private: void OnNetworkConnected_n(const NetworkInformation& network_info); @@ -115,6 +120,9 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface, void OnNetworkPreference_n(NetworkType type, rtc::NetworkPreference preference); + absl::optional FindNetworkHandleFromIfname( + const std::string& ifname) const; + const int android_sdk_int_; ScopedJavaGlobalRef j_application_context_; ScopedJavaGlobalRef j_network_monitor_; @@ -133,6 +141,14 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorInterface, bool find_network_handle_without_ipv6_temporary_part_ RTC_GUARDED_BY(network_thread_) = false; bool surface_cellular_types_ RTC_GUARDED_BY(network_thread_) = false; + + // NOTE: if bind_using_ifname_ is TRUE + // then the adapter name is used with substring matching as follows: + // An adapater name repored by android as 'wlan0' + // will be matched with 'v4-wlan0' ("v4-wlan0".find("wlan0") != npos). + // This applies to adapter_type_by_name_, vpn_underlying_adapter_type_by_name_ + // and FindNetworkHandleFromIfname. + bool bind_using_ifname_ RTC_GUARDED_BY(network_thread_) = true; const rtc::scoped_refptr safety_flag_ RTC_PT_GUARDED_BY(network_thread_); };