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