summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-16 19:06:03 +0000
committerjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-16 19:06:03 +0000
commit0f8f1b43ed25b241a8cf0a9c28c1185a79dad402 (patch)
treebce894104c5c7345c1ee36a985d5de97a18bac20 /net
parentc1d3c6fc04293d8fac98412cfc818ddd5978d0c4 (diff)
downloadchromium_src-0f8f1b43ed25b241a8cf0a9c28c1185a79dad402.zip
chromium_src-0f8f1b43ed25b241a8cf0a9c28c1185a79dad402.tar.gz
chromium_src-0f8f1b43ed25b241a8cf0a9c28c1185a79dad402.tar.bz2
Refine IPv6 probe to require that the client has an IPv6 address on an interface
This currently only works on Posix, not windows. Network changes are monitored, and the test is repeated each time interfaces change (which is a subset of any IP addresses changing). The test performed is performed on a worker thread, so latency should not be an issue (even if we created much slower tests). The current test appears to takes in the raneg of 50-100ms, and probably (under the covers) does some reading from files). BUG=25680 BUG=12754 r=wtc,eroman Review URL: http://codereview.chromium.org/1006001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41743 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/host_resolver_impl.cc132
-rw-r--r--net/base/host_resolver_impl.h24
-rw-r--r--net/base/net_util.cc70
3 files changed, 205 insertions, 21 deletions
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc
index d5dce2f..2120332 100644
--- a/net/base/host_resolver_impl.cc
+++ b/net/base/host_resolver_impl.cc
@@ -21,6 +21,7 @@
#include "net/base/host_resolver_proc.h"
#include "net/base/net_log.h"
#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
#include "net/base/network_change_notifier.h"
#if defined(OS_WIN)
@@ -192,7 +193,8 @@ class HostResolverImpl::Job
public:
Job(int id, HostResolverImpl* resolver, const Key& key,
RequestsTrace* requests_trace)
- : id_(id), key_(key),
+ : id_(id),
+ key_(key),
resolver_(resolver),
origin_loop_(MessageLoop::current()),
resolver_proc_(resolver->effective_resolver_proc()),
@@ -421,6 +423,91 @@ class HostResolverImpl::Job
//-----------------------------------------------------------------------------
+// This class represents a request to the worker pool for a "probe for IPv6
+// support" call.
+class HostResolverImpl::IPv6ProbeJob
+ : public base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob> {
+ public:
+ explicit IPv6ProbeJob(HostResolverImpl* resolver)
+ : resolver_(resolver),
+ origin_loop_(MessageLoop::current()) {
+ }
+
+ void Start() {
+ DCHECK(IsOnOriginThread());
+ const bool IS_SLOW = true;
+ WorkerPool::PostTask(
+ FROM_HERE, NewRunnableMethod(this, &IPv6ProbeJob::DoProbe), IS_SLOW);
+ }
+
+ // Cancels the current job.
+ void Cancel() {
+ DCHECK(IsOnOriginThread());
+ resolver_ = NULL; // Read/write ONLY on origin thread.
+ {
+ AutoLock locked(origin_loop_lock_);
+ // Origin loop may be destroyed before we can use it!
+ origin_loop_ = NULL;
+ }
+ }
+
+ bool was_cancelled() const {
+ DCHECK(IsOnOriginThread());
+ return resolver_ == NULL;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob>;
+
+ ~IPv6ProbeJob() {
+ }
+
+ // Run on worker thread.
+ void DoProbe() {
+ // Do actual testing on this thread, as it takes 40-100ms.
+ AddressFamily family = IPv6Supported() ? ADDRESS_FAMILY_UNSPECIFIED
+ : ADDRESS_FAMILY_IPV4;
+
+ Task* reply = NewRunnableMethod(this, &IPv6ProbeJob::OnProbeComplete,
+ family);
+
+ // The origin loop could go away while we are trying to post to it, so we
+ // need to call its PostTask method inside a lock. See ~HostResolver.
+ {
+ AutoLock locked(origin_loop_lock_);
+ if (origin_loop_) {
+ origin_loop_->PostTask(FROM_HERE, reply);
+ return;
+ }
+ }
+
+ // We didn't post, so delete the reply.
+ delete reply;
+ }
+
+ // Callback for when DoProbe() completes (runs on origin thread).
+ void OnProbeComplete(AddressFamily address_family) {
+ DCHECK(IsOnOriginThread());
+ if (!was_cancelled())
+ resolver_->IPv6ProbeSetDefaultAddressFamily(address_family);
+ }
+
+ bool IsOnOriginThread() const {
+ return !MessageLoop::current() || origin_loop_ == MessageLoop::current();
+ }
+
+ // Used/set only on origin thread.
+ HostResolverImpl* resolver_;
+
+ // Used to post ourselves onto the origin thread.
+ Lock origin_loop_lock_;
+ MessageLoop* origin_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(IPv6ProbeJob);
+};
+
+//-----------------------------------------------------------------------------
+
// We rely on the priority enum values being sequential having starting at 0,
// and increasing for lower priorities.
COMPILE_ASSERT(HIGHEST == 0u &&
@@ -581,7 +668,8 @@ HostResolverImpl::HostResolverImpl(
resolver_proc_(resolver_proc),
default_address_family_(ADDRESS_FAMILY_UNSPECIFIED),
shutdown_(false),
- network_change_notifier_(network_change_notifier) {
+ network_change_notifier_(network_change_notifier),
+ ipv6_probe_monitoring_(false) {
DCHECK_GT(max_jobs, 0u);
// It is cumbersome to expose all of the constraints in the constructor,
@@ -598,6 +686,8 @@ HostResolverImpl::HostResolverImpl(
HostResolverImpl::~HostResolverImpl() {
// Cancel the outstanding jobs. Those jobs may contain several attached
// requests, which will also be cancelled.
+ DiscardIPv6ProbeJob();
+
for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
it->second->Cancel();
@@ -742,6 +832,18 @@ void HostResolverImpl::RemoveObserver(HostResolver::Observer* observer) {
observers_.erase(it);
}
+void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) {
+ ipv6_probe_monitoring_ = false;
+ DiscardIPv6ProbeJob();
+ default_address_family_ = address_family;
+}
+
+void HostResolverImpl::ProbeIPv6Support() {
+ DCHECK(!ipv6_probe_monitoring_);
+ ipv6_probe_monitoring_ = true;
+ OnIPAddressChanged(); // Give initial setup call.
+}
+
void HostResolverImpl::Shutdown() {
shutdown_ = true;
@@ -975,6 +1077,32 @@ void HostResolverImpl::OnCancelRequest(const BoundNetLog& net_log,
void HostResolverImpl::OnIPAddressChanged() {
if (cache_.get())
cache_->clear();
+ if (ipv6_probe_monitoring_) {
+ DiscardIPv6ProbeJob();
+ ipv6_probe_job_ = new IPv6ProbeJob(this);
+ ipv6_probe_job_->Start();
+ }
+}
+
+void HostResolverImpl::DiscardIPv6ProbeJob() {
+ if (ipv6_probe_job_.get()) {
+ ipv6_probe_job_->Cancel();
+ ipv6_probe_job_ = NULL;
+ }
+}
+
+void HostResolverImpl::IPv6ProbeSetDefaultAddressFamily(
+ AddressFamily address_family) {
+ DCHECK(address_family == ADDRESS_FAMILY_UNSPECIFIED ||
+ address_family == ADDRESS_FAMILY_IPV4);
+ if (default_address_family_ != address_family)
+ LOG(INFO) << "IPv6Probe forced AddressFamily setting to "
+ << ((address_family == ADDRESS_FAMILY_UNSPECIFIED)
+ ? "ADDRESS_FAMILY_UNSPECIFIED"
+ : "ADDRESS_FAMILY_IPV4");
+ default_address_family_ = address_family;
+ // Drop reference since the job has called us back.
+ DiscardIPv6ProbeJob();
}
// static
diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h
index de2cc14..9652072 100644
--- a/net/base/host_resolver_impl.h
+++ b/net/base/host_resolver_impl.h
@@ -89,9 +89,12 @@ class HostResolverImpl : public HostResolver,
virtual void AddObserver(HostResolver::Observer* observer);
virtual void RemoveObserver(HostResolver::Observer* observer);
- virtual void SetDefaultAddressFamily(AddressFamily address_family) {
- default_address_family_ = address_family;
- }
+ // Set address family, and disable IPv6 probe support.
+ virtual void SetDefaultAddressFamily(AddressFamily address_family);
+
+ // Continuously observe whether IPv6 is supported, and set the allowable
+ // address family to IPv4 iff IPv6 is not supported.
+ void ProbeIPv6Support();
virtual HostResolverImpl* GetAsHostResolverImpl() { return this; }
@@ -130,6 +133,7 @@ class HostResolverImpl : public HostResolver,
private:
class Job;
class JobPool;
+ class IPv6ProbeJob;
class Request;
class RequestsTrace;
typedef std::vector<Request*> RequestsList;
@@ -179,6 +183,12 @@ class HostResolverImpl : public HostResolver,
// NetworkChangeNotifier::Observer methods:
virtual void OnIPAddressChanged();
+ // Notify IPv6ProbeJob not to call back, and discard reference to the job.
+ void DiscardIPv6ProbeJob();
+
+ // Callback from IPv6 probe activity.
+ void IPv6ProbeSetDefaultAddressFamily(AddressFamily address_family);
+
// Returns true if the constraints for |pool| are met, and a new job can be
// created for this pool.
bool CanCreateJobForPool(const JobPool& pool) const;
@@ -247,6 +257,14 @@ class HostResolverImpl : public HostResolver,
scoped_refptr<RequestsTrace> requests_trace_;
+ // Indicate if probing is done after each network change event to set address
+ // family.
+ // When false, explicit setting of address family is used.
+ bool ipv6_probe_monitoring_;
+
+ // The last un-cancelled IPv6ProbeJob (if any).
+ scoped_refptr<IPv6ProbeJob> ipv6_probe_job_;
+
DISALLOW_COPY_AND_ASSIGN(HostResolverImpl);
};
diff --git a/net/base/net_util.cc b/net/base/net_util.cc
index 843c80a..29d804e 100644
--- a/net/base/net_util.cc
+++ b/net/base/net_util.cc
@@ -21,9 +21,11 @@
#include <ws2tcpip.h>
#include <wspiapi.h> // Needed for Win2k compat.
#elif defined(OS_POSIX)
+#include <fcntl.h>
+#include <ifaddrs.h>
#include <netdb.h>
+#include <net/if.h>
#include <sys/socket.h>
-#include <fcntl.h>
#endif
#include "base/base64.h"
@@ -258,7 +260,7 @@ bool DecodeBQEncoding(const std::string& part, RFC2047EncodingType enc_type,
bool DecodeWord(const std::string& encoded_word,
const std::string& referrer_charset,
- bool *is_rfc2047,
+ bool* is_rfc2047,
std::string* output) {
if (!IsStringASCII(encoded_word)) {
// Try UTF-8, referrer_charset and the native OS default charset in turn.
@@ -1552,15 +1554,21 @@ void SetExplicitlyAllowedPorts(const std::wstring& allowed_ports) {
enum IPv6SupportStatus {
IPV6_CANNOT_CREATE_SOCKETS,
IPV6_CAN_CREATE_SOCKETS,
+ IPV6_GETIFADDRS_FAILED,
+ IPV6_GLOBAL_ADDRESS_MISSING,
+ IPV6_GLOBAL_ADDRESS_PRESENT,
IPV6_SUPPORT_MAX // Bounding values for enumeration.
};
static void IPv6SupportResults(IPv6SupportStatus result) {
static bool run_once = false;
- if (run_once)
- return;
- run_once = true;
- UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status", result, IPV6_SUPPORT_MAX);
+ if (!run_once) {
+ run_once = true;
+ UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status", result, IPV6_SUPPORT_MAX);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status_retest", result,
+ IPV6_SUPPORT_MAX);
+ }
}
// TODO(jar): The following is a simple estimate of IPv6 support. We may need
@@ -1568,27 +1576,58 @@ static void IPv6SupportResults(IPv6SupportStatus result) {
// static
bool IPv6Supported() {
#if defined(OS_POSIX)
- int test_socket;
-
- test_socket = socket(AF_INET6, SOCK_STREAM, 0);
+ int test_socket = socket(AF_INET6, SOCK_STREAM, 0);
if (test_socket == -1) {
IPv6SupportResults(IPV6_CANNOT_CREATE_SOCKETS);
return false;
}
-
close(test_socket);
- IPv6SupportResults(IPV6_CAN_CREATE_SOCKETS);
+
+ // Check to see if any interface has a IPv6 address.
+ struct ifaddrs* interface_addr = NULL;
+ int rv = getifaddrs(&interface_addr);
+ if (rv != 0) {
+ IPv6SupportResults(IPV6_GETIFADDRS_FAILED);
+ return true; // Don't yet block IPv6.
+ }
+
+ bool found_ipv6 = false;
+ for (struct ifaddrs* interface = interface_addr;
+ interface != NULL;
+ interface = interface->ifa_next) {
+ if (!(IFF_UP & interface->ifa_flags))
+ continue;
+ if (IFF_LOOPBACK & interface->ifa_flags)
+ continue;
+ struct sockaddr* addr = interface->ifa_addr;
+ if (!addr)
+ continue;
+ if (addr->sa_family != AF_INET6)
+ continue;
+ // Safe cast since this is AF_INET6.
+ struct sockaddr_in6* addr_in6 =
+ reinterpret_cast<struct sockaddr_in6*>(addr);
+ struct in6_addr* sin6_addr = &addr_in6->sin6_addr;
+ if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr))
+ continue;
+ found_ipv6 = true;
+ break;
+ }
+ freeifaddrs(interface_addr);
+ if (!found_ipv6) {
+ IPv6SupportResults(IPV6_GLOBAL_ADDRESS_MISSING);
+ return false;
+ }
+
+ IPv6SupportResults(IPV6_GLOBAL_ADDRESS_PRESENT);
return true;
#elif defined(OS_WIN)
EnsureWinsockInit();
- SOCKET test_socket;
-
- test_socket = socket(AF_INET6, SOCK_STREAM, 0);
+ SOCKET test_socket = socket(AF_INET6, SOCK_STREAM, 0);
if (test_socket == INVALID_SOCKET) {
IPv6SupportResults(IPV6_CANNOT_CREATE_SOCKETS);
return false;
}
-
closesocket(test_socket);
IPv6SupportResults(IPV6_CAN_CREATE_SOCKETS);
return true;
@@ -1598,5 +1637,4 @@ bool IPv6Supported() {
#endif // defined(various platforms)
}
-
} // namespace net