diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-19 18:44:58 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-19 18:44:58 +0000 |
commit | 6d603aab761645693dbf88cfe12c1964c7920ad2 (patch) | |
tree | 4e2494978df12617d574fef7930f80582aa22463 /net | |
parent | 3a38fe9996db9d25c0c44ee68afdb2620c50b6b1 (diff) | |
download | chromium_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.cc | 83 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.h | 27 | ||||
-rw-r--r-- | net/base/network_change_notifier_netlink_linux.cc | 96 | ||||
-rw-r--r-- | net/base/network_change_notifier_netlink_linux.h | 20 | ||||
-rwxr-xr-x | net/net.gyp | 2 |
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', |