15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2010 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/network_change_notifier_linux.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <errno.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/socket.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/eintr_wrapper.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/task.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/network_change_notifier_netlink_linux.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net { 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kInvalidSocket = -1; 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NetworkChangeNotifierLinux::Thread 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : public base::Thread, public MessageLoopForIO::Watcher { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Thread(); 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual ~Thread(); 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // MessageLoopForIO::Watcher: 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnFileCanReadWithoutBlocking(int fd); 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnFileCanWriteWithoutBlocking(int /* fd */); 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected: 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // base::Thread 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void Init(); 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void CleanUp(); 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void NotifyObserversOfIPAddressChange() { 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NetworkChangeNotifier::NotifyObserversOfIPAddressChange(); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Starts listening for netlink messages. Also handles the messages if there 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // are any available on the netlink socket. 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void ListenForNotifications(); 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Attempts to read from the netlink socket into |buf| of length |len|. 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Returns the bytes read on synchronous success and ERR_IO_PENDING if the 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // recv() would block. Otherwise, it returns a net error code. 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int ReadNotificationMessage(char* buf, size_t len); 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The netlink socket descriptor. 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int netlink_fd_; 56 MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; 57 58 // Technically only needed for ChromeOS, but it's ugly to #ifdef out. 59 ScopedRunnableMethodFactory<Thread> method_factory_; 60 61 DISALLOW_COPY_AND_ASSIGN(Thread); 62}; 63 64NetworkChangeNotifierLinux::Thread::Thread() 65 : base::Thread("NetworkChangeNotifier"), 66 netlink_fd_(kInvalidSocket), 67 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} 68 69NetworkChangeNotifierLinux::Thread::~Thread() {} 70 71void NetworkChangeNotifierLinux::Thread::Init() { 72 netlink_fd_ = InitializeNetlinkSocket(); 73 if (netlink_fd_ < 0) { 74 netlink_fd_ = kInvalidSocket; 75 return; 76 } 77 ListenForNotifications(); 78} 79 80void NetworkChangeNotifierLinux::Thread::CleanUp() { 81 if (netlink_fd_ != kInvalidSocket) { 82 if (HANDLE_EINTR(close(netlink_fd_)) != 0) 83 PLOG(ERROR) << "Failed to close socket"; 84 netlink_fd_ = kInvalidSocket; 85 netlink_watcher_.StopWatchingFileDescriptor(); 86 } 87} 88 89void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { 90 DCHECK_EQ(fd, netlink_fd_); 91 ListenForNotifications(); 92} 93 94void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( 95 int /* fd */) { 96 NOTREACHED(); 97} 98 99void NetworkChangeNotifierLinux::Thread::ListenForNotifications() { 100 char buf[4096]; 101 int rv = ReadNotificationMessage(buf, arraysize(buf)); 102 while (rv > 0) { 103 if (HandleNetlinkMessage(buf, rv)) { 104 VLOG(1) << "Detected IP address changes."; 105#if defined(OS_CHROMEOS) 106 // TODO(oshima): chromium-os:8285 - introduced artificial delay to 107 // work around the issue of network load issue after connection 108 // restored. See the bug for more details. 109 // This should be removed once this bug is properly fixed. 110 const int kObserverNotificationDelayMS = 200; 111 message_loop()->PostDelayedTask( 112 FROM_HERE, 113 method_factory_.NewRunnableMethod( 114 &Thread::NotifyObserversOfIPAddressChange), 115 kObserverNotificationDelayMS); 116#else 117 NotifyObserversOfIPAddressChange(); 118#endif 119 } 120 rv = ReadNotificationMessage(buf, arraysize(buf)); 121 } 122 123 if (rv == ERR_IO_PENDING) { 124 rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false, 125 MessageLoopForIO::WATCH_READ, &netlink_watcher_, this); 126 LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_; 127 } 128} 129 130int NetworkChangeNotifierLinux::Thread::ReadNotificationMessage( 131 char* buf, 132 size_t len) { 133 DCHECK_NE(len, 0u); 134 DCHECK(buf); 135 memset(buf, 0, sizeof(buf)); 136 int rv = recv(netlink_fd_, buf, len, 0); 137 if (rv > 0) 138 return rv; 139 140 DCHECK_NE(rv, 0); 141 if (errno != EAGAIN && errno != EWOULDBLOCK) { 142 PLOG(DFATAL) << "recv"; 143 return ERR_FAILED; 144 } 145 146 return ERR_IO_PENDING; 147} 148 149NetworkChangeNotifierLinux::NetworkChangeNotifierLinux() 150 : notifier_thread_(new Thread) { 151 // We create this notifier thread because the notification implementation 152 // needs a MessageLoopForIO, and there's no guarantee that 153 // MessageLoop::current() meets that criterion. 154 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); 155 notifier_thread_->StartWithOptions(thread_options); 156} 157 158NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { 159 // We don't need to explicitly Stop(), but doing so allows us to sanity- 160 // check that the notifier thread shut down properly. 161 notifier_thread_->Stop(); 162} 163 164bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const { 165 // TODO(eroman): http://crbug.com/53473 166 return false; 167} 168 169} // namespace net 170