diff --git a/talk/app/webrtc/fakeportallocatorfactory.h b/talk/app/webrtc/fakeportallocatorfactory.h index 6da8e88dfa..f326b62043 100644 --- a/talk/app/webrtc/fakeportallocatorfactory.h +++ b/talk/app/webrtc/fakeportallocatorfactory.h @@ -60,6 +60,8 @@ class FakePortAllocatorFactory : public PortAllocatorFactoryInterface { return turn_configs_; } + void SetNetworkIgnoreMask(int network_ignore_mask) {} + protected: FakePortAllocatorFactory() {} ~FakePortAllocatorFactory() {} diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index 23aa65288f..9c8d7088de 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -3088,6 +3088,23 @@ JOW(jlong, PeerConnectionFactory_nativeCreateAudioTrack)( return (jlong)track.release(); } +JOW(void, PeerConnectionFactory_nativeSetOptions)( + JNIEnv* jni, jclass, jlong native_factory, jobject options) { + rtc::scoped_refptr factory( + factoryFromJava(native_factory)); + jclass options_class = jni->GetObjectClass(options); + jfieldID network_ignore_mask_field = + jni->GetFieldID(options_class, "networkIgnoreMask", "I"); + int network_ignore_mask = + jni->GetIntField(options, network_ignore_mask_field); + PeerConnectionFactoryInterface::Options options_to_set; + + // This doesn't necessarily match the c++ version of this struct; feel free + // to add more parameters as necessary. + options_to_set.network_ignore_mask = network_ignore_mask; + factory->SetOptions(options_to_set); +} + static void JavaIceServersToJsepIceServers( JNIEnv* jni, jobject j_ice_servers, PeerConnectionInterface::IceServers* ice_servers) { diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java index 3d2ec5f742..3687daae2d 100644 --- a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java +++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java @@ -41,6 +41,18 @@ public class PeerConnectionFactory { private final long nativeFactory; + static class Options { + // Keep in sync with webrtc/base/network.h! + static final int ADAPTER_TYPE_UNKNOWN = 0; + static final int ADAPTER_TYPE_ETHERNET = 1 << 0; + static final int ADAPTER_TYPE_WIFI = 1 << 1; + static final int ADAPTER_TYPE_CELLULAR = 1 << 2; + static final int ADAPTER_TYPE_VPN = 1 << 3; + static final int ADAPTER_TYPE_LOOPBACK = 1 << 4; + + public int networkIgnoreMask; + } + // |context| is an android.content.Context object, but we keep it untyped here // to allow building on non-Android platforms. // Callers may specify either |initializeAudio| or |initializeVideo| as false @@ -105,10 +117,16 @@ public class PeerConnectionFactory { nativeFactory, id, source.nativeSource)); } + public void setOptions(Options options) { + nativeSetOptions(nativeFactory, options); + } + public void dispose() { freeFactory(nativeFactory); } + public native void nativeSetOptions(long nativeFactory, Options options); + private static native long nativeCreatePeerConnectionFactory(); private static native long nativeCreateObserver( diff --git a/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java b/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java index c3ec72c4c7..ab0510a8cf 100644 --- a/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java +++ b/talk/app/webrtc/java/testcommon/src/org/webrtc/PeerConnectionTest.java @@ -106,6 +106,7 @@ public class PeerConnectionTest { @Override public synchronized void onIceCandidate(IceCandidate candidate) { --expectedIceCandidates; + // We don't assert expectedIceCandidates >= 0 because it's hard to know // how many to expect, in general. We only use expectIceCandidates to // assert a minimal count. @@ -508,6 +509,12 @@ public class PeerConnectionTest { // EnumSet.of(Logging.TraceLevel.TRACE_ALL), // Logging.Severity.LS_SENSITIVE); + // Allow loopback interfaces too since our Android devices often don't + // have those. + PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); + options.networkIgnoreMask = 0; + factory.setOptions(options); + MediaConstraints pcConstraints = new MediaConstraints(); pcConstraints.mandatory.add( new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); diff --git a/talk/app/webrtc/peerconnectionfactory.cc b/talk/app/webrtc/peerconnectionfactory.cc index 1191e5ae77..852f4d9b14 100644 --- a/talk/app/webrtc/peerconnectionfactory.cc +++ b/talk/app/webrtc/peerconnectionfactory.cc @@ -129,7 +129,7 @@ PeerConnectionFactory::PeerConnectionFactory( PeerConnectionFactory::~PeerConnectionFactory() { DCHECK(signaling_thread_->IsCurrent()); channel_manager_.reset(NULL); - allocator_factory_ = NULL; + default_allocator_factory_ = NULL; if (owns_ptrs_) { if (wraps_current_thread_) rtc::ThreadManager::Instance()->UnwrapCurrentThread(); @@ -141,8 +141,8 @@ bool PeerConnectionFactory::Initialize() { DCHECK(signaling_thread_->IsCurrent()); rtc::InitRandom(rtc::Time()); - allocator_factory_ = PortAllocatorFactory::Create(worker_thread_); - if (!allocator_factory_) + default_allocator_factory_ = PortAllocatorFactory::Create(worker_thread_); + if (!default_allocator_factory_) return false; cricket::DummyDeviceManager* device_manager( @@ -196,13 +196,18 @@ PeerConnectionFactory::CreatePeerConnection( DTLSIdentityServiceInterface* dtls_identity_service, PeerConnectionObserver* observer) { DCHECK(signaling_thread_->IsCurrent()); - DCHECK(allocator_factory || allocator_factory_); + DCHECK(allocator_factory || default_allocator_factory_); + + PortAllocatorFactoryInterface* chosen_allocator_factory = + allocator_factory ? allocator_factory : default_allocator_factory_.get(); + chosen_allocator_factory->SetNetworkIgnoreMask(options_.network_ignore_mask); + rtc::scoped_refptr pc( new rtc::RefCountedObject(this)); if (!pc->Initialize( configuration, constraints, - allocator_factory ? allocator_factory : allocator_factory_.get(), + chosen_allocator_factory, dtls_identity_service, observer)) { return NULL; diff --git a/talk/app/webrtc/peerconnectionfactory.h b/talk/app/webrtc/peerconnectionfactory.h index e35c4476b0..f0d01392f6 100644 --- a/talk/app/webrtc/peerconnectionfactory.h +++ b/talk/app/webrtc/peerconnectionfactory.h @@ -97,7 +97,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface { rtc::Thread* signaling_thread_; rtc::Thread* worker_thread_; Options options_; - rtc::scoped_refptr allocator_factory_; + rtc::scoped_refptr default_allocator_factory_; // External Audio device used for audio playback. rtc::scoped_refptr default_adm_; rtc::scoped_ptr channel_manager_; diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h index 1b52a5615e..391255ff24 100644 --- a/talk/app/webrtc/peerconnectioninterface.h +++ b/talk/app/webrtc/peerconnectioninterface.h @@ -78,6 +78,7 @@ #include "talk/app/webrtc/statstypes.h" #include "talk/app/webrtc/umametrics.h" #include "webrtc/base/fileutils.h" +#include "webrtc/base/network.h" #include "webrtc/base/socketaddress.h" namespace rtc { @@ -421,6 +422,12 @@ class PortAllocatorFactoryInterface : public rtc::RefCountInterface { const std::vector& stun_servers, const std::vector& turn_configurations) = 0; + // TODO(phoglund): Make pure virtual when Chrome's factory implements this. + // After this method is called, the port allocator should consider loopback + // network interfaces as well. + virtual void SetNetworkIgnoreMask(int network_ignore_mask) { + } + protected: PortAllocatorFactoryInterface() {} ~PortAllocatorFactoryInterface() {} @@ -482,10 +489,16 @@ class PeerConnectionFactoryInterface : public rtc::RefCountInterface { public: Options() : disable_encryption(false), - disable_sctp_data_channels(false) { + disable_sctp_data_channels(false), + network_ignore_mask(rtc::kDefaultNetworkIgnoreMask) { } bool disable_encryption; bool disable_sctp_data_channels; + + // Sets the network types to ignore. For instance, calling this with + // ADAPTER_TYPE_ETHERNET | ADAPTER_TYPE_LOOPBACK will ignore Ethernet and + // loopback interfaces. + int network_ignore_mask; }; virtual void SetOptions(const Options& options) = 0; diff --git a/talk/app/webrtc/portallocatorfactory.cc b/talk/app/webrtc/portallocatorfactory.cc index dd86239a56..bd6caccc80 100644 --- a/talk/app/webrtc/portallocatorfactory.cc +++ b/talk/app/webrtc/portallocatorfactory.cc @@ -52,6 +52,10 @@ PortAllocatorFactory::PortAllocatorFactory(rtc::Thread* worker_thread) PortAllocatorFactory::~PortAllocatorFactory() {} +void PortAllocatorFactory::SetNetworkIgnoreMask(int network_ignore_mask) { + network_manager_->set_network_ignore_mask(network_ignore_mask); +} + cricket::PortAllocator* PortAllocatorFactory::CreatePortAllocator( const std::vector& stun, const std::vector& turn) { diff --git a/talk/app/webrtc/portallocatorfactory.h b/talk/app/webrtc/portallocatorfactory.h index 33887b4441..83376d0b84 100644 --- a/talk/app/webrtc/portallocatorfactory.h +++ b/talk/app/webrtc/portallocatorfactory.h @@ -56,6 +56,8 @@ class PortAllocatorFactory : public PortAllocatorFactoryInterface { const std::vector& stun, const std::vector& turn); + virtual void SetNetworkIgnoreMask(int network_ignore_mask); + protected: explicit PortAllocatorFactory(rtc::Thread* worker_thread); ~PortAllocatorFactory(); diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc index 499df667b9..a6a2e13ec0 100644 --- a/talk/app/webrtc/statscollector.cc +++ b/talk/app/webrtc/statscollector.cc @@ -55,6 +55,7 @@ const char* STATSREPORT_ADAPTER_TYPE_ETHERNET = "lan"; const char* STATSREPORT_ADAPTER_TYPE_WIFI = "wlan"; const char* STATSREPORT_ADAPTER_TYPE_WWAN = "wwan"; const char* STATSREPORT_ADAPTER_TYPE_VPN = "vpn"; +const char* STATSREPORT_ADAPTER_TYPE_LOOPBACK = "loopback"; bool GetTransportIdFromProxy(const cricket::ProxyTransportMap& map, const std::string& proxy, @@ -359,6 +360,8 @@ const char* AdapterTypeToStatsType(rtc::AdapterType type) { return STATSREPORT_ADAPTER_TYPE_WWAN; case rtc::ADAPTER_TYPE_VPN: return STATSREPORT_ADAPTER_TYPE_VPN; + case rtc::ADAPTER_TYPE_LOOPBACK: + return STATSREPORT_ADAPTER_TYPE_LOOPBACK; default: ASSERT(false); return ""; diff --git a/webrtc/base/network.cc b/webrtc/base/network.cc index 5152a6bc45..07180d51c7 100644 --- a/webrtc/base/network.cc +++ b/webrtc/base/network.cc @@ -120,8 +120,10 @@ std::string AdapterTypeToString(AdapterType type) { return "Cellular"; case ADAPTER_TYPE_VPN: return "VPN"; + case ADAPTER_TYPE_LOOPBACK: + return "Loopback"; default: - ASSERT(false); + DCHECK(false) << "Invalid type " << type; return std::string(); } } @@ -268,6 +270,7 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, BasicNetworkManager::BasicNetworkManager() : thread_(NULL), sent_first_update_(false), start_count_(0), + network_ignore_mask_(kDefaultNetworkIgnoreMask), ignore_non_default_routes_(false) { } @@ -331,15 +334,19 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, prefix, prefix_length); auto existing_network = current_networks.find(key); if (existing_network == current_networks.end()) { + AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN; + if (cursor->ifa_flags & IFF_LOOPBACK) { + // TODO(phoglund): Need to recognize other types as well. + adapter_type = ADAPTER_TYPE_LOOPBACK; + } scoped_ptr network(new Network(cursor->ifa_name, cursor->ifa_name, prefix, - prefix_length)); + prefix_length, + adapter_type)); network->set_scope_id(scope_id); network->AddIP(ip); - bool ignored = ((cursor->ifa_flags & IFF_LOOPBACK) || - IsIgnoredNetwork(*network)); - network->set_ignored(ignored); + network->set_ignored(IsIgnoredNetwork(*network)); if (include_ignored || !network->ignored()) { networks->push_back(network.release()); } @@ -477,15 +484,20 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, std::string key = MakeNetworkKey(name, prefix, prefix_length); auto existing_network = current_networks.find(key); if (existing_network == current_networks.end()) { + AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN; + if (adapter_addrs->IfType == IF_TYPE_SOFTWARE_LOOPBACK) { + // TODO(phoglund): Need to recognize other types as well. + adapter_type = ADAPTER_TYPE_LOOPBACK; + } scoped_ptr network(new Network(name, description, prefix, - prefix_length)); + prefix_length, + adapter_type)); network->set_scope_id(scope_id); network->AddIP(ip); - bool ignore = ((adapter_addrs->IfType == IF_TYPE_SOFTWARE_LOOPBACK) || - IsIgnoredNetwork(*network)); - network->set_ignored(ignore); + bool ignored = IsIgnoredNetwork(*network); + network->set_ignored(ignored); if (include_ignored || !network->ignored()) { networks->push_back(network.release()); } @@ -537,6 +549,10 @@ bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) const { return true; } } + + if (network_ignore_mask_ & network.type()) { + return true; + } #if defined(WEBRTC_POSIX) // Filter out VMware/VirtualBox interfaces, typically named vmnet1, // vmnet8, or vboxnet0. diff --git a/webrtc/base/network.h b/webrtc/base/network.h index 7238aa050f..cd013001b9 100644 --- a/webrtc/base/network.h +++ b/webrtc/base/network.h @@ -33,12 +33,16 @@ class Thread; enum AdapterType { // This enum resembles the one in Chromium net::ConnectionType. ADAPTER_TYPE_UNKNOWN = 0, - ADAPTER_TYPE_ETHERNET = 1, - ADAPTER_TYPE_WIFI = 2, - ADAPTER_TYPE_CELLULAR = 3, - ADAPTER_TYPE_VPN = 4 + ADAPTER_TYPE_ETHERNET = 1 << 0, + ADAPTER_TYPE_WIFI = 1 << 1, + ADAPTER_TYPE_CELLULAR = 1 << 2, + ADAPTER_TYPE_VPN = 1 << 3, + ADAPTER_TYPE_LOOPBACK = 1 << 4 }; +// By default, ignore loopback interfaces on the host. +const int kDefaultNetworkIgnoreMask = ADAPTER_TYPE_LOOPBACK; + // Makes a string key for this network. Used in the network manager's maps. // Network objects are keyed on interface name, network prefix and the // length of that prefix. @@ -61,9 +65,9 @@ class NetworkManager { sigslot::signal0<> SignalError; // Start/Stop monitoring of network interfaces - // list. SignalNetworksChanged or SignalError is emitted immidiately + // list. SignalNetworksChanged or SignalError is emitted immediately // after StartUpdating() is called. After that SignalNetworksChanged - // is emitted wheneven list of networks changes. + // is emitted whenever list of networks changes. virtual void StartUpdating() = 0; virtual void StopUpdating() = 0; @@ -143,11 +147,24 @@ class BasicNetworkManager : public NetworkManagerBase, virtual void OnMessage(Message* msg); bool started() { return start_count_ > 0; } - // Sets the network ignore list, which is empty by default. Any network on - // the ignore list will be filtered from network enumeration results. + // Sets the network ignore list, which is empty by default. Any network on the + // ignore list will be filtered from network enumeration results. void set_network_ignore_list(const std::vector& list) { network_ignore_list_ = list; } + + // Sets the network types to ignore. For instance, calling this with + // ADAPTER_TYPE_ETHERNET | ADAPTER_TYPE_LOOPBACK will ignore Ethernet and + // loopback interfaces. Set to kDefaultNetworkIgnoreMask by default. + void set_network_ignore_mask(int network_ignore_mask) { + // TODO(phoglund): implement support for other types than loopback. + // See https://code.google.com/p/webrtc/issues/detail?id=4288. + // Then remove set_network_ignore_list. + network_ignore_mask_ = network_ignore_mask; + } + + int network_ignore_mask() const { return network_ignore_mask_; } + #if defined(WEBRTC_LINUX) // Sets the flag for ignoring non-default routes. void set_ignore_non_default_routes(bool value) { @@ -178,6 +195,7 @@ class BasicNetworkManager : public NetworkManagerBase, bool sent_first_update_; int start_count_; std::vector network_ignore_list_; + int network_ignore_mask_; bool ignore_non_default_routes_; }; diff --git a/webrtc/base/network_unittest.cc b/webrtc/base/network_unittest.cc index 36edcc0867..662dc70dff 100644 --- a/webrtc/base/network_unittest.cc +++ b/webrtc/base/network_unittest.cc @@ -81,16 +81,51 @@ TEST_F(NetworkTest, TestNetworkConstruct) { } // Tests that our ignore function works properly. -TEST_F(NetworkTest, TestNetworkIgnore) { +TEST_F(NetworkTest, TestIsIgnoredNetworkIgnoresOnlyLoopbackByDefault) { Network ipv4_network1("test_eth0", "Test Network Adapter 1", - IPAddress(0x12345600U), 24); + IPAddress(0x12345600U), 24, ADAPTER_TYPE_ETHERNET); + Network ipv4_network2("test_wlan0", "Test Network Adapter 2", + IPAddress(0x12345601U), 16, ADAPTER_TYPE_WIFI); + Network ipv4_network3("test_cell0", "Test Network Adapter 3", + IPAddress(0x12345602U), 16, ADAPTER_TYPE_CELLULAR); + Network ipv4_network4("test_vpn0", "Test Network Adapter 4", + IPAddress(0x12345603U), 16, ADAPTER_TYPE_VPN); + Network ipv4_network5("test_lo", "Test Network Adapter 5", + IPAddress(0x12345604U), 16, ADAPTER_TYPE_LOOPBACK); + BasicNetworkManager network_manager; + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network2)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network3)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network4)); + EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network5)); +} + +TEST_F(NetworkTest, TestIsIgnoredNetworkIgnoresIPsStartingWith0) { + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24, ADAPTER_TYPE_ETHERNET); Network ipv4_network2("test_eth1", "Test Network Adapter 2", - IPAddress(0x00010000U), 16); + IPAddress(0x010000U), 24, ADAPTER_TYPE_ETHERNET); BasicNetworkManager network_manager; EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1)); EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2)); } +TEST_F(NetworkTest, TestIsIgnoredNetworkIgnoresNetworksAccordingToIgnoreMask) { + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24, ADAPTER_TYPE_ETHERNET); + Network ipv4_network2("test_wlan0", "Test Network Adapter 2", + IPAddress(0x12345601U), 16, ADAPTER_TYPE_WIFI); + Network ipv4_network3("test_cell0", "Test Network Adapter 3", + IPAddress(0x12345602U), 16, ADAPTER_TYPE_CELLULAR); + BasicNetworkManager network_manager; + network_manager.set_network_ignore_mask( + ADAPTER_TYPE_ETHERNET | ADAPTER_TYPE_LOOPBACK | ADAPTER_TYPE_WIFI); + EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network1)); + EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network3)); +} + +// TODO(phoglund): Remove when ignore list goes away. TEST_F(NetworkTest, TestIgnoreList) { Network ignore_me("ignore_me", "Ignore me please!", IPAddress(0x12345600U), 24);