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