summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-19 18:44:58 +0000
committerwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-19 18:44:58 +0000
commit6d603aab761645693dbf88cfe12c1964c7920ad2 (patch)
tree4e2494978df12617d574fef7930f80582aa22463 /net
parent3a38fe9996db9d25c0c44ee68afdb2620c50b6b1 (diff)
downloadchromium_src-6d603aab761645693dbf88cfe12c1964c7920ad2.zip
chromium_src-6d603aab761645693dbf88cfe12c1964c7920ad2.tar.gz
chromium_src-6d603aab761645693dbf88cfe12c1964c7920ad2.tar.bz2
Re-lands r39417: "Implement NetworkChangeNotifierLinux."
The change broke certain linux buildbots due to netlink.h forward declaring struct net, which conflicts with the net namespace. The fix is to move the netlink code into a separate file which is not in the net namespace. Review URL: http://codereview.chromium.org/650009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39465 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/network_change_notifier_linux.cc83
-rw-r--r--net/base/network_change_notifier_linux.h27
-rw-r--r--net/base/network_change_notifier_netlink_linux.cc96
-rw-r--r--net/base/network_change_notifier_netlink_linux.h20
-rwxr-xr-xnet/net.gyp2
5 files changed, 224 insertions, 4 deletions
diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc
index e6eba24..2a85c15 100644
--- a/net/base/network_change_notifier_linux.cc
+++ b/net/base/network_change_notifier_linux.cc
@@ -4,9 +4,88 @@
#include "net/base/network_change_notifier_linux.h"
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_change_notifier_netlink_linux.h"
+
namespace net {
-NetworkChangeNotifierLinux::NetworkChangeNotifierLinux() {}
-NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {}
+namespace {
+
+const int kInvalidSocket = -1;
+
+} // namespace
+
+NetworkChangeNotifierLinux::NetworkChangeNotifierLinux()
+ : netlink_fd_(kInvalidSocket),
+ loop_(MessageLoopForIO::current()) {
+ netlink_fd_ = InitializeNetlinkSocket();
+ if (netlink_fd_ < 0) {
+ netlink_fd_ = kInvalidSocket;
+ return;
+ }
+
+ ListenForNotifications();
+}
+
+NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
+ if (netlink_fd_ != kInvalidSocket) {
+ if (close(netlink_fd_) != 0)
+ PLOG(ERROR) << "Failed to close socket";
+ netlink_fd_ = kInvalidSocket;
+ netlink_watcher_.StopWatchingFileDescriptor();
+ }
+}
+
+void NetworkChangeNotifierLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(fd, netlink_fd_);
+
+ ListenForNotifications();
+}
+
+void NetworkChangeNotifierLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {
+ NOTREACHED();
+}
+
+void NetworkChangeNotifierLinux::ListenForNotifications() {
+ char buf[4096];
+ int rv = ReadNotificationMessage(buf, arraysize(buf));
+ while (rv > 0 ) {
+ if (HandleNetlinkMessage(buf, rv))
+ helper_.OnIPAddressChanged();
+ rv = ReadNotificationMessage(buf, arraysize(buf));
+ }
+
+ if (rv == ERR_IO_PENDING) {
+ rv = loop_->WatchFileDescriptor(
+ netlink_fd_, false, MessageLoopForIO::WATCH_READ, &netlink_watcher_,
+ this);
+ LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_;
+ }
+}
+
+int NetworkChangeNotifierLinux::ReadNotificationMessage(char* buf, size_t len) {
+ DCHECK_NE(len, 0u);
+ DCHECK(buf);
+
+ memset(buf, 0, sizeof(buf));
+ int rv = recv(netlink_fd_, buf, len, 0);
+ if (rv > 0) {
+ return rv;
+ } else {
+ DCHECK_NE(rv, 0);
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ PLOG(DFATAL) << "recv";
+ return ERR_FAILED;
+ }
+
+ return ERR_IO_PENDING;
+ }
+}
} // namespace net
diff --git a/net/base/network_change_notifier_linux.h b/net/base/network_change_notifier_linux.h
index 78111c9..323ea55 100644
--- a/net/base/network_change_notifier_linux.h
+++ b/net/base/network_change_notifier_linux.h
@@ -6,14 +6,18 @@
#define NET_BASE_NETWORK_CHANGE_NOTIFIER_LINUX_H_
#include "base/basictypes.h"
+#include "base/message_loop.h"
#include "net/base/network_change_notifier_helper.h"
namespace net {
-class NetworkChangeNotifierLinux : public NetworkChangeNotifier {
+class NetworkChangeNotifierLinux
+ : public NetworkChangeNotifier, public MessageLoopForIO::Watcher {
public:
NetworkChangeNotifierLinux();
+ // NetworkChangeNotifier methods:
+
virtual void AddObserver(Observer* observer) {
helper_.AddObserver(observer);
}
@@ -22,13 +26,32 @@ class NetworkChangeNotifierLinux : public NetworkChangeNotifier {
helper_.RemoveObserver(observer);
}
+ // MessageLoopForIO::Watcher methods:
+
+ virtual void OnFileCanReadWithoutBlocking(int fd);
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */);
+
private:
virtual ~NetworkChangeNotifierLinux();
- void OnIPAddressChanged() { helper_.OnIPAddressChanged(); }
+ // Starts listening for netlink messages. Also handles the messages if there
+ // are any available on the netlink socket.
+ void ListenForNotifications();
+
+ // Attempts to read from the netlink socket into |buf| of length |len|.
+ // Returns the bytes read on synchronous success and ERR_IO_PENDING if the
+ // recv() would block. Otherwise, it returns a net error code.
+ int ReadNotificationMessage(char* buf, size_t len);
+
+ // Handles the netlink message and notifies the observers.
+ void HandleNotifications(const char* buf, size_t len);
internal::NetworkChangeNotifierHelper helper_;
+ int netlink_fd_; // This is the netlink socket descriptor.
+ MessageLoopForIO* const loop_;
+ MessageLoopForIO::FileDescriptorWatcher netlink_watcher_;
+
DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierLinux);
};
diff --git a/net/base/network_change_notifier_netlink_linux.cc b/net/base/network_change_notifier_netlink_linux.cc
new file mode 100644
index 0000000..9d82203
--- /dev/null
+++ b/net/base/network_change_notifier_netlink_linux.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/network_change_notifier_netlink_linux.h"
+
+#include <fcntl.h>
+// socket.h is needed to define types for the linux kernel header netlink.h
+// so it needs to come before netlink.h.
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace {
+
+// Return true on success, false on failure.
+// Too small a function to bother putting in a library?
+bool SetNonBlocking(int fd) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (-1 == flags)
+ return false;
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0 ? true : false;
+}
+
+} // namespace
+
+int InitializeNetlinkSocket() {
+ int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0) {
+ PLOG(ERROR) << "Error creating netlink socket";
+ return -1;
+ }
+
+ if (!SetNonBlocking(sock)) {
+ PLOG(ERROR) << "Failed to set netlink socket to non-blocking mode.";
+ if (close(sock) != 0)
+ PLOG(ERROR) << "Failed to close socket";
+ return -1;
+ }
+
+ struct sockaddr_nl local_addr;
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr.nl_family = AF_NETLINK;
+ local_addr.nl_pid = getpid();
+ local_addr.nl_groups =
+ RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY;
+ int ret = bind(sock, reinterpret_cast<struct sockaddr*>(&local_addr),
+ sizeof(local_addr));
+ if (ret < 0) {
+ PLOG(ERROR) << "Error binding netlink socket";
+ if (close(sock) != 0)
+ PLOG(ERROR) << "Failed to close socket";
+ return -1;
+ }
+
+ return sock;
+}
+
+bool HandleNetlinkMessage(char* buf, size_t len) {
+ const struct nlmsghdr* netlink_message_header =
+ reinterpret_cast<struct nlmsghdr*>(buf);
+ DCHECK(netlink_message_header);
+ for (; NLMSG_OK(netlink_message_header, len);
+ netlink_message_header = NLMSG_NEXT(netlink_message_header, len)) {
+ int netlink_message_type = netlink_message_header->nlmsg_type;
+ switch (netlink_message_type) {
+ case NLMSG_DONE:
+ NOTREACHED()
+ << "This is a monitoring netlink socket. It should never be done.";
+ return false;
+ case NLMSG_ERROR:
+ LOG(ERROR) << "Unexpected netlink error.";
+ return false;
+ // During IP address changes, we will see all these messages. Only fire
+ // the notification when we get a new address or remove an address. We
+ // may still end up notifying observers more than strictly necessary, but
+ // if the primary interface goes down and back up, then this is necessary.
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ return true;
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ return false;
+ default:
+ LOG(DFATAL) << "Received unexpected netlink message type: "
+ << netlink_message_type;
+ return false;
+ }
+ }
+
+ return false;
+}
diff --git a/net/base/network_change_notifier_netlink_linux.h b/net/base/network_change_notifier_netlink_linux.h
new file mode 100644
index 0000000..c06fdc7
--- /dev/null
+++ b/net/base/network_change_notifier_netlink_linux.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is to hide the netlink implementation details since the netlink.h
+// header contains a struct net; which conflicts with the net namespace. So we
+// separate out all the netlink stuff into these files.
+
+#ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
+#define NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
+
+#include <cstddef>
+
+// Returns the file descriptor if successful. Otherwise, returns -1.
+int InitializeNetlinkSocket();
+
+// Returns true if a network change has been detected, otherwise returns false.
+bool HandleNetlinkMessage(char* buf, size_t len);
+
+#endif // NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
diff --git a/net/net.gyp b/net/net.gyp
index 599f683..4509616 100755
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -113,6 +113,8 @@
'base/network_change_notifier_linux.h',
'base/network_change_notifier_mac.cc',
'base/network_change_notifier_mac.h',
+ 'base/network_change_notifier_netlink_linux.cc',
+ 'base/network_change_notifier_netlink_linux.h',
'base/network_change_notifier_win.cc',
'base/network_change_notifier_win.h',
'base/nss_memio.c',