1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be 3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file. 4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/network_change_notifier_linux.h" 6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <errno.h> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sys/socket.h> 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 10731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/compiler_specific.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/eintr_wrapper.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/task.h" 133f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_errors.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/network_change_notifier_netlink_linux.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net { 18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kInvalidSocket = -1; 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 25731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickclass NetworkChangeNotifierLinux::Thread 26731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick : public base::Thread, public MessageLoopForIO::Watcher { 27731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick public: 28731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick Thread(); 29731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick virtual ~Thread(); 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 31731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // MessageLoopForIO::Watcher: 32731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick virtual void OnFileCanReadWithoutBlocking(int fd); 33731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick virtual void OnFileCanWriteWithoutBlocking(int /* fd */); 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 35731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick protected: 36731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // base::Thread 37731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick virtual void Init(); 38731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick virtual void CleanUp(); 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 40731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick private: 41731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick void NotifyObserversOfIPAddressChange() { 42731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick NetworkChangeNotifier::NotifyObserversOfIPAddressChange(); 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 45731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Starts listening for netlink messages. Also handles the messages if there 46731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // are any available on the netlink socket. 47731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick void ListenForNotifications(); 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Attempts to read from the netlink socket into |buf| of length |len|. 50731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Returns the bytes read on synchronous success and ERR_IO_PENDING if the 51731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // recv() would block. Otherwise, it returns a net error code. 52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick int ReadNotificationMessage(char* buf, size_t len); 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 54731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // The netlink socket descriptor. 55731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick int netlink_fd_; 56731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 58731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Technically only needed for ChromeOS, but it's ugly to #ifdef out. 59731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ScopedRunnableMethodFactory<Thread> method_factory_; 60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DISALLOW_COPY_AND_ASSIGN(Thread); 62731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}; 63731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 64731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickNetworkChangeNotifierLinux::Thread::Thread() 65731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick : base::Thread("NetworkChangeNotifier"), 66731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick netlink_fd_(kInvalidSocket), 67731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 69731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickNetworkChangeNotifierLinux::Thread::~Thread() {} 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 71731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid NetworkChangeNotifierLinux::Thread::Init() { 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch netlink_fd_ = InitializeNetlinkSocket(); 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (netlink_fd_ < 0) { 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch netlink_fd_ = kInvalidSocket; 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ListenForNotifications(); 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 80731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid NetworkChangeNotifierLinux::Thread::CleanUp() { 81731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (netlink_fd_ != kInvalidSocket) { 82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (HANDLE_EINTR(close(netlink_fd_)) != 0) 83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick PLOG(ERROR) << "Failed to close socket"; 84731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick netlink_fd_ = kInvalidSocket; 85731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick netlink_watcher_.StopWatchingFileDescriptor(); 86731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 87731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 88731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 89731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { 90731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK_EQ(fd, netlink_fd_); 91731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ListenForNotifications(); 92731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 93731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 943f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( 953f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen int /* fd */) { 96731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick NOTREACHED(); 97731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 99731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid NetworkChangeNotifierLinux::Thread::ListenForNotifications() { 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch char buf[4096]; 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int rv = ReadNotificationMessage(buf, arraysize(buf)); 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (rv > 0) { 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (HandleNetlinkMessage(buf, rv)) { 104731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Detected IP address changes."; 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_CHROMEOS) 106201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // TODO(oshima): chromium-os:8285 - introduced artificial delay to 107201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // work around the issue of network load issue after connection 108201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // restored. See the bug for more details. 109201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // This should be removed once this bug is properly fixed. 110201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch const int kObserverNotificationDelayMS = 200; 111731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick message_loop()->PostDelayedTask( 112731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick FROM_HERE, 113731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick method_factory_.NewRunnableMethod( 114731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick &Thread::NotifyObserversOfIPAddressChange), 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kObserverNotificationDelayMS); 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#else 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotifyObserversOfIPAddressChange(); 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch rv = ReadNotificationMessage(buf, arraysize(buf)); 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (rv == ERR_IO_PENDING) { 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false, 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MessageLoopForIO::WATCH_READ, &netlink_watcher_, this); 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_; 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 130731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickint NetworkChangeNotifierLinux::Thread::ReadNotificationMessage( 131731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick char* buf, 132731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick size_t len) { 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK_NE(len, 0u); 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(buf); 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch memset(buf, 0, sizeof(buf)); 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int rv = recv(netlink_fd_, buf, len, 0); 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (rv > 0) 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return rv; 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK_NE(rv, 0); 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (errno != EAGAIN && errno != EWOULDBLOCK) { 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PLOG(DFATAL) << "recv"; 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return ERR_FAILED; 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return ERR_IO_PENDING; 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 149731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickNetworkChangeNotifierLinux::NetworkChangeNotifierLinux() 150731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick : notifier_thread_(new Thread) { 151731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // We create this notifier thread because the notification implementation 152731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // needs a MessageLoopForIO, and there's no guarantee that 153731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // MessageLoop::current() meets that criterion. 154731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); 155731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick notifier_thread_->StartWithOptions(thread_options); 156731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 157731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 158731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickNetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { 159731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // We don't need to explicitly Stop(), but doing so allows us to sanity- 160731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // check that the notifier thread shut down properly. 161731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick notifier_thread_->Stop(); 162731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 163731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 164731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickbool NetworkChangeNotifierLinux::IsCurrentlyOffline() const { 165731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // TODO(eroman): http://crbug.com/53473 166731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return false; 167731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 168731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} // namespace net 170