Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Side by Side Diff: net/base/address_tracker_linux.cc

Issue 11359141: Use Netlink instead of NetworkManager (via D-bus) to monitor network (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Little reformat Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/base/address_tracker_linux.h" 5 #include "net/base/address_tracker_linux.h"
6 6
7 #include <linux/if.h>
8
7 #include <errno.h> 9 #include <errno.h>
8 10
9 #include "base/logging.h" 11 #include "base/logging.h"
10 #include "base/posix/eintr_wrapper.h" 12 #include "base/posix/eintr_wrapper.h"
13 #include "base/threading/thread_restrictions.h"
11 #include "net/base/network_change_notifier_linux.h" 14 #include "net/base/network_change_notifier_linux.h"
12 15
13 namespace net { 16 namespace net {
14 namespace internal { 17 namespace internal {
15 18
16 namespace { 19 namespace {
17 20
18 // Retrieves address from NETLINK address message. 21 // Retrieves address from NETLINK address message.
19 bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) { 22 bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
20 const struct ifaddrmsg* msg = 23 const struct ifaddrmsg* msg =
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 } 59 }
57 } 60 }
58 if (local) 61 if (local)
59 address = local; 62 address = local;
60 if (!address) 63 if (!address)
61 return false; 64 return false;
62 out->assign(address, address + address_length); 65 out->assign(address, address + address_length);
63 return true; 66 return true;
64 } 67 }
65 68
66 void CloseSocket(int fd) {
67 if (HANDLE_EINTR(close(fd)) < 0)
68 PLOG(ERROR) << "Could not close NETLINK socket.";
69 }
70
71 } // namespace 69 } // namespace
72 70
73 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& callback) 71 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
74 : callback_(callback), 72 const base::Closure& link_callback)
75 netlink_fd_(-1) { 73 : address_callback_(address_callback),
76 DCHECK(!callback.is_null()); 74 link_callback_(link_callback),
75 netlink_fd_(-1),
76 is_offline_(true),
77 is_offline_initialized_(false),
78 is_offline_initialized_cv_(&is_offline_lock_) {
79 DCHECK(!address_callback.is_null());
80 DCHECK(!link_callback.is_null());
77 } 81 }
78 82
79 AddressTrackerLinux::~AddressTrackerLinux() { 83 AddressTrackerLinux::~AddressTrackerLinux() {
80 if (netlink_fd_ >= 0) 84 CloseSocket();
81 CloseSocket(netlink_fd_);
82 } 85 }
83 86
84 void AddressTrackerLinux::Init() { 87 void AddressTrackerLinux::Init() {
85 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 88 netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
86 if (sock < 0) { 89 if (netlink_fd_ < 0) {
87 PLOG(ERROR) << "Could not create NETLINK socket"; 90 PLOG(ERROR) << "Could not create NETLINK socket";
91 AbortAndForceOnline();
88 return; 92 return;
89 } 93 }
90 94
91 // Request notifications. 95 // Request notifications.
92 struct sockaddr_nl addr = {}; 96 struct sockaddr_nl addr = {};
93 addr.nl_family = AF_NETLINK; 97 addr.nl_family = AF_NETLINK;
94 addr.nl_pid = getpid(); 98 addr.nl_pid = getpid();
95 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993 99 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993
96 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY; 100 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
97 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); 101 RTMGRP_LINK;
102 int rv = bind(netlink_fd_,
103 reinterpret_cast<struct sockaddr*>(&addr),
104 sizeof(addr));
98 if (rv < 0) { 105 if (rv < 0) {
99 PLOG(ERROR) << "Could not bind NETLINK socket"; 106 PLOG(ERROR) << "Could not bind NETLINK socket";
100 CloseSocket(sock); 107 AbortAndForceOnline();
101 return; 108 return;
102 } 109 }
103 110
104 // Watch for asynchronous messages.
105 if (SetNonBlocking(sock)) {
106 PLOG(ERROR) << "Could not make NETLINK socket non-blocking";
107 CloseSocket(sock);
108 return;
109 }
110
111 rv = MessageLoopForIO::current()->WatchFileDescriptor(
112 sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
113 if (rv < 0) {
114 PLOG(ERROR) << "Could not watch NETLINK socket";
115 CloseSocket(sock);
116 return;
117 }
118
119 // Request dump of addresses. 111 // Request dump of addresses.
120 struct sockaddr_nl peer = {}; 112 struct sockaddr_nl peer = {};
121 peer.nl_family = AF_NETLINK; 113 peer.nl_family = AF_NETLINK;
122 114
123 struct { 115 struct {
124 struct nlmsghdr header; 116 struct nlmsghdr header;
125 struct rtgenmsg msg; 117 struct rtgenmsg msg;
126 } request = {}; 118 } request = {};
127 119
128 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg)); 120 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request));
129 request.header.nlmsg_type = RTM_GETADDR; 121 request.header.nlmsg_type = RTM_GETADDR;
130 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 122 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
131 request.header.nlmsg_pid = getpid(); 123 request.header.nlmsg_pid = getpid();
132 request.msg.rtgen_family = AF_UNSPEC; 124 request.msg.rtgen_family = AF_UNSPEC;
133 125
134 rv = HANDLE_EINTR(sendto(sock, &request, request.header.nlmsg_len, 0, 126 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
127 0, reinterpret_cast<struct sockaddr*>(&peer),
128 sizeof(peer)));
129 if (rv < 0) {
130 PLOG(ERROR) << "Could not send NETLINK request";
131 AbortAndForceOnline();
132 return;
133 }
134
135 // Consume pending message to populate the AddressMap, but don't notify.
136 // Sending another request without first reading responses results in EBUSY.
137 bool address_changed;
138 bool link_changed;
139 ReadMessages(&address_changed, &link_changed);
140
141 // Request dump of link state
142 request.header.nlmsg_type = RTM_GETLINK;
143
144 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
135 reinterpret_cast<struct sockaddr*>(&peer), 145 reinterpret_cast<struct sockaddr*>(&peer),
136 sizeof(peer))); 146 sizeof(peer)));
137 if (rv < 0) { 147 if (rv < 0) {
138 PLOG(ERROR) << "Could not send NETLINK request"; 148 PLOG(ERROR) << "Could not send NETLINK request";
139 CloseSocket(sock); 149 AbortAndForceOnline();
140 return; 150 return;
141 } 151 }
142 152
143 netlink_fd_ = sock; 153 // Consume pending message to populate links_online_, but don't notify.
144 154 ReadMessages(&address_changed, &link_changed);
145 // Consume any pending messages to populate the AddressMap, but don't notify. 155 {
146 ReadMessages(); 156 base::AutoLock lock(is_offline_lock_);
157 is_offline_initialized_ = true;
158 is_offline_initialized_cv_.Signal();
159 }
160
161 rv = MessageLoopForIO::current()->WatchFileDescriptor(
162 netlink_fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
163 if (rv < 0) {
164 PLOG(ERROR) << "Could not watch NETLINK socket";
165 AbortAndForceOnline();
166 return;
167 }
168 }
169
170 void AddressTrackerLinux::AbortAndForceOnline() {
171 CloseSocket();
172 base::AutoLock lock(is_offline_lock_);
173 is_offline_ = false;
174 is_offline_initialized_ = true;
175 is_offline_initialized_cv_.Signal();
147 } 176 }
148 177
149 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { 178 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
150 base::AutoLock lock(lock_); 179 base::AutoLock lock(address_map_lock_);
151 return map_; 180 return address_map_;
152 } 181 }
153 182
154 bool AddressTrackerLinux::ReadMessages() { 183 NetworkChangeNotifier::ConnectionType
184 AddressTrackerLinux::GetCurrentConnectionType() {
185 // http://crbug.com/125097
186 base::ThreadRestrictions::ScopedAllowWait allow_wait;
187 base::AutoLock lock(is_offline_lock_);
188 // Make sure the initial offline state is set before returning.
189 while (!is_offline_initialized_) {
190 is_offline_initialized_cv_.Wait();
191 }
192 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
193 // http://crbug.com/160537
194 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
195 NetworkChangeNotifier::CONNECTION_UNKNOWN;
196 }
197
198 void AddressTrackerLinux::ReadMessages(bool* address_changed,
199 bool* link_changed) {
200 *address_changed = false;
201 *link_changed = false;
155 char buffer[4096]; 202 char buffer[4096];
156 bool changed = false; 203 bool first_loop = true;
157 for (;;) { 204 for (;;) {
158 int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 0)); 205 int rv = HANDLE_EINTR(recv(netlink_fd_,
206 buffer,
207 sizeof(buffer),
208 // Block the first time through loop.
209 first_loop ? 0 : MSG_DONTWAIT));
210 first_loop = false;
159 if (rv == 0) { 211 if (rv == 0) {
160 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; 212 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
161 return false; 213 return;
162 } 214 }
163 if (rv < 0) { 215 if (rv < 0) {
164 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 216 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
165 break; 217 break;
166 PLOG(ERROR) << "Failed to recv from netlink socket"; 218 PLOG(ERROR) << "Failed to recv from netlink socket";
167 return false; 219 return;
168 } 220 }
169 changed |= HandleMessage(buffer, rv); 221 HandleMessage(buffer, rv, address_changed, link_changed);
170 }; 222 };
171 return changed; 223 if (*link_changed) {
172 } 224 base::AutoLock lock(is_offline_lock_);
173 225 is_offline_ = online_links_.empty();
174 bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) { 226 }
227 }
228
229 void AddressTrackerLinux::HandleMessage(const char* buffer,
230 size_t length,
231 bool* address_changed,
232 bool* link_changed) {
175 DCHECK(buffer); 233 DCHECK(buffer);
176 bool changed = false;
177 for (const struct nlmsghdr* header = 234 for (const struct nlmsghdr* header =
178 reinterpret_cast<const struct nlmsghdr*>(buffer); 235 reinterpret_cast<const struct nlmsghdr*>(buffer);
179 NLMSG_OK(header, length); 236 NLMSG_OK(header, length);
180 header = NLMSG_NEXT(header, length)) { 237 header = NLMSG_NEXT(header, length)) {
181 switch (header->nlmsg_type) { 238 switch (header->nlmsg_type) {
182 case NLMSG_DONE: 239 case NLMSG_DONE:
183 return changed; 240 return;
184 case NLMSG_ERROR: 241 case NLMSG_ERROR: {
185 LOG(ERROR) << "Unexpected netlink error."; 242 const struct nlmsgerr* msg =
186 return changed; 243 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
244 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
245 } return;
187 case RTM_NEWADDR: { 246 case RTM_NEWADDR: {
188 IPAddressNumber address; 247 IPAddressNumber address;
189 if (GetAddress(header, &address)) { 248 if (GetAddress(header, &address)) {
190 base::AutoLock lock(lock_); 249 base::AutoLock lock(address_map_lock_);
191 const struct ifaddrmsg* msg = 250 const struct ifaddrmsg* msg =
192 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); 251 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
193 // Only indicate change if the address is new or ifaddrmsg info has 252 // Only indicate change if the address is new or ifaddrmsg info has
194 // changed. 253 // changed.
195 AddressMap::iterator it = map_.find(address); 254 AddressMap::iterator it = address_map_.find(address);
196 if (it == map_.end()) { 255 if (it == address_map_.end()) {
197 map_.insert(it, std::make_pair(address, *msg)); 256 address_map_.insert(it, std::make_pair(address, *msg));
198 changed = true; 257 *address_changed = true;
199 } else if (memcmp(&it->second, msg, sizeof(*msg))) { 258 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
200 it->second = *msg; 259 it->second = *msg;
201 changed = true; 260 *address_changed = true;
202 } 261 }
203 } 262 }
204 } break; 263 } break;
205 case RTM_DELADDR: { 264 case RTM_DELADDR: {
206 IPAddressNumber address; 265 IPAddressNumber address;
207 if (GetAddress(header, &address)) { 266 if (GetAddress(header, &address)) {
208 base::AutoLock lock(lock_); 267 base::AutoLock lock(address_map_lock_);
209 if (map_.erase(address)) 268 if (address_map_.erase(address))
210 changed = true; 269 *address_changed = true;
211 } 270 }
212 } break; 271 } break;
272 case RTM_NEWLINK: {
273 const struct ifinfomsg* msg =
274 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
275 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
276 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
277 if (online_links_.insert(msg->ifi_index).second)
278 *link_changed = true;
279 } else {
280 if (online_links_.erase(msg->ifi_index))
281 *link_changed = true;
282 }
283 } break;
284 case RTM_DELLINK: {
285 const struct ifinfomsg* msg =
286 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
287 if (online_links_.erase(msg->ifi_index))
288 *link_changed = true;
289 } break;
213 default: 290 default:
214 break; 291 break;
215 } 292 }
216 } 293 }
217 return changed;
218 } 294 }
219 295
220 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { 296 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
221 DCHECK_EQ(netlink_fd_, fd); 297 DCHECK_EQ(netlink_fd_, fd);
222 if (ReadMessages()) 298 bool address_changed;
223 callback_.Run(); 299 bool link_changed;
300 ReadMessages(&address_changed, &link_changed);
301 if (address_changed)
302 address_callback_.Run();
303 if (link_changed)
304 link_callback_.Run();
224 } 305 }
225 306
226 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} 307 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
227 308
309 void AddressTrackerLinux::CloseSocket() {
310 if (netlink_fd_ >= 0 && HANDLE_EINTR(close(netlink_fd_)) < 0)
311 PLOG(ERROR) << "Could not close NETLINK socket.";
312 netlink_fd_ = -1;
313 }
314
228 } // namespace internal 315 } // namespace internal
229 } // namespace net 316 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698