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

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: Handle failed init gracefully Created 8 years, 1 month 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/eintr_wrapper.h" 11 #include "base/eintr_wrapper.h"
10 #include "base/logging.h" 12 #include "base/logging.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_); 85 }
86
87 void AddressTrackerLinux::GetInitialSettings() {
88 // Request dump of addresses.
89 struct sockaddr_nl peer = {};
90 peer.nl_family = AF_NETLINK;
91
92 struct {
93 struct nlmsghdr header;
94 struct rtgenmsg msg;
95 } request = {};
96
97 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request));
98 request.header.nlmsg_type = RTM_GETADDR;
99 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
100 request.header.nlmsg_pid = getpid();
101 request.msg.rtgen_family = AF_UNSPEC;
102
103 int rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
104 0, reinterpret_cast<struct sockaddr*>(&peer),
105 sizeof(peer)));
106 if (rv < 0) {
107 PLOG(ERROR) << "Could not send NETLINK request";
108 AbortAndForceOnline();
109 return;
110 }
111
112 // Consume pending message to populate the AddressMap, but don't notify.
113 // Sending another request without first reading responses results in EBUSY.
114 bool address_changed;
115 bool link_changed;
116 ReadMessages(&address_changed, &link_changed);
117
118 // Request dump of link state
119 request.header.nlmsg_type = RTM_GETLINK;
120
121 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
122 reinterpret_cast<struct sockaddr*>(&peer),
123 sizeof(peer)));
124 if (rv < 0) {
125 PLOG(ERROR) << "Could not send NETLINK request";
126 AbortAndForceOnline();
127 return;
128 }
129
130 // Consume pending message to populate links_online_, but don't notify.
131 ReadMessages(&address_changed, &link_changed);
82 } 132 }
83 133
84 void AddressTrackerLinux::Init() { 134 void AddressTrackerLinux::Init() {
85 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 135 netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
86 if (sock < 0) { 136 if (netlink_fd_ < 0) {
87 PLOG(ERROR) << "Could not create NETLINK socket"; 137 PLOG(ERROR) << "Could not create NETLINK socket";
138 AbortAndForceOnline();
88 return; 139 return;
89 } 140 }
90 141
91 // Request notifications. 142 // Request notifications.
92 struct sockaddr_nl addr = {}; 143 struct sockaddr_nl addr = {};
93 addr.nl_family = AF_NETLINK; 144 addr.nl_family = AF_NETLINK;
94 addr.nl_pid = getpid(); 145 addr.nl_pid = getpid();
95 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993 146 // 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; 147 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
97 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); 148 RTMGRP_LINK;
149 int rv = bind(netlink_fd_,
150 reinterpret_cast<struct sockaddr*>(&addr),
151 sizeof(addr));
98 if (rv < 0) { 152 if (rv < 0) {
99 PLOG(ERROR) << "Could not bind NETLINK socket"; 153 PLOG(ERROR) << "Could not bind NETLINK socket";
100 CloseSocket(sock); 154 AbortAndForceOnline();
101 return; 155 return;
102 } 156 }
103 157
104 // Watch for asynchronous messages. 158 GetInitialSettings();
105 if (SetNonBlocking(sock)) { 159 {
106 PLOG(ERROR) << "Could not make NETLINK socket non-blocking"; 160 base::AutoLock lock(is_offline_lock_);
szym 2012/11/16 17:26:17 This should be closer to where is_offline_ is firs
107 CloseSocket(sock); 161 is_offline_initialized_ = true;
108 return; 162 is_offline_initialized_cv_.Signal();
109 } 163 }
110 164
111 rv = MessageLoopForIO::current()->WatchFileDescriptor( 165 rv = MessageLoopForIO::current()->WatchFileDescriptor(
112 sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this); 166 netlink_fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
113 if (rv < 0) { 167 if (rv < 0) {
114 PLOG(ERROR) << "Could not watch NETLINK socket"; 168 PLOG(ERROR) << "Could not watch NETLINK socket";
115 CloseSocket(sock); 169 AbortAndForceOnline();
116 return; 170 return;
117 } 171 }
172 }
118 173
119 // Request dump of addresses. 174 void AddressTrackerLinux::AbortAndForceOnline() {
120 struct sockaddr_nl peer = {}; 175 CloseSocket();
121 peer.nl_family = AF_NETLINK; 176 base::AutoLock lock(is_offline_lock_);
122 177 is_offline_ = false;
123 struct { 178 is_offline_initialized_ = true;
124 struct nlmsghdr header; 179 is_offline_initialized_cv_.Signal();
125 struct rtgenmsg msg;
126 } request = {};
127
128 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
129 request.header.nlmsg_type = RTM_GETADDR;
130 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
131 request.header.nlmsg_pid = getpid();
132 request.msg.rtgen_family = AF_UNSPEC;
133
134 rv = HANDLE_EINTR(sendto(sock, &request, request.header.nlmsg_len, 0,
135 reinterpret_cast<struct sockaddr*>(&peer),
136 sizeof(peer)));
137 if (rv < 0) {
138 PLOG(ERROR) << "Could not send NETLINK request";
139 CloseSocket(sock);
140 return;
141 }
142
143 netlink_fd_ = sock;
144
145 // Consume any pending messages to populate the AddressMap, but don't notify.
146 ReadMessages();
147 } 180 }
148 181
149 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { 182 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
150 base::AutoLock lock(lock_); 183 base::AutoLock lock(address_map_lock_);
151 return map_; 184 return address_map_;
152 } 185 }
153 186
154 bool AddressTrackerLinux::ReadMessages() { 187 NetworkChangeNotifier::ConnectionType
188 AddressTrackerLinux::GetCurrentConnectionType() {
189 // http://crbug.com/125097
190 base::ThreadRestrictions::ScopedAllowWait allow_wait;
191 base::AutoLock lock(is_offline_lock_);
192 // Make sure the initial offline state is set before returning.
193 while (!is_offline_initialized_) {
194 is_offline_initialized_cv_.Wait();
195 }
196 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
197 // http://crbug.com/160537
198 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
199 NetworkChangeNotifier::CONNECTION_UNKNOWN;
200 }
201
202 void AddressTrackerLinux::ReadMessages(bool* address_changed,
203 bool* link_changed) {
204 *address_changed = false;
205 *link_changed = false;
155 char buffer[4096]; 206 char buffer[4096];
156 bool changed = false; 207 bool first_loop = true;
157 for (;;) { 208 for (;;) {
158 int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 0)); 209 int rv = HANDLE_EINTR(recv(netlink_fd_,
210 buffer,
211 sizeof(buffer),
212 // Block the first time through loop.
213 first_loop ? 0 : MSG_DONTWAIT));
214 first_loop = false;
159 if (rv == 0) { 215 if (rv == 0) {
160 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; 216 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
161 return false; 217 return;
162 } 218 }
163 if (rv < 0) { 219 if (rv < 0) {
164 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 220 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
165 break; 221 break;
166 PLOG(ERROR) << "Failed to recv from netlink socket"; 222 PLOG(ERROR) << "Failed to recv from netlink socket";
167 return false; 223 return;
168 } 224 }
169 changed |= HandleMessage(buffer, rv); 225 HandleMessage(buffer, rv, address_changed, link_changed);
170 }; 226 };
171 return changed; 227 if (*link_changed) {
228 base::AutoLock lock(is_offline_lock_);
229 is_offline_ = online_links_.empty();
230 }
172 } 231 }
173 232
174 bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) { 233 void AddressTrackerLinux::HandleMessage(const char* buffer,
234 size_t length,
235 bool* address_changed,
236 bool* link_changed) {
175 DCHECK(buffer); 237 DCHECK(buffer);
176 bool changed = false;
177 for (const struct nlmsghdr* header = 238 for (const struct nlmsghdr* header =
178 reinterpret_cast<const struct nlmsghdr*>(buffer); 239 reinterpret_cast<const struct nlmsghdr*>(buffer);
179 NLMSG_OK(header, length); 240 NLMSG_OK(header, length);
180 header = NLMSG_NEXT(header, length)) { 241 header = NLMSG_NEXT(header, length)) {
181 switch (header->nlmsg_type) { 242 switch (header->nlmsg_type) {
182 case NLMSG_DONE: 243 case NLMSG_DONE:
183 return changed; 244 return;
184 case NLMSG_ERROR: 245 case NLMSG_ERROR: {
185 LOG(ERROR) << "Unexpected netlink error."; 246 const struct nlmsgerr* msg =
186 return changed; 247 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
248 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
249 } return;
187 case RTM_NEWADDR: { 250 case RTM_NEWADDR: {
188 IPAddressNumber address; 251 IPAddressNumber address;
189 if (GetAddress(header, &address)) { 252 if (GetAddress(header, &address)) {
190 base::AutoLock lock(lock_); 253 base::AutoLock lock(address_map_lock_);
191 const struct ifaddrmsg* msg = 254 const struct ifaddrmsg* msg =
192 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); 255 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
193 // Only indicate change if the address is new or ifaddrmsg info has 256 // Only indicate change if the address is new or ifaddrmsg info has
194 // changed. 257 // changed.
195 AddressMap::iterator it = map_.find(address); 258 AddressMap::iterator it = address_map_.find(address);
196 if (it == map_.end()) { 259 if (it == address_map_.end()) {
197 map_.insert(it, std::make_pair(address, *msg)); 260 address_map_.insert(it, std::make_pair(address, *msg));
198 changed = true; 261 *address_changed = true;
199 } else if (memcmp(&it->second, msg, sizeof(*msg))) { 262 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
200 it->second = *msg; 263 it->second = *msg;
201 changed = true; 264 *address_changed = true;
202 } 265 }
203 } 266 }
204 } break; 267 } break;
205 case RTM_DELADDR: { 268 case RTM_DELADDR: {
206 IPAddressNumber address; 269 IPAddressNumber address;
207 if (GetAddress(header, &address)) { 270 if (GetAddress(header, &address)) {
208 base::AutoLock lock(lock_); 271 base::AutoLock lock(address_map_lock_);
209 if (map_.erase(address)) 272 if (address_map_.erase(address))
210 changed = true; 273 *address_changed = true;
211 } 274 }
212 } break; 275 } break;
276 case RTM_NEWLINK: {
277 const struct ifinfomsg* msg =
278 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
279 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
280 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
281 if (online_links_.insert(msg->ifi_index).second)
282 *link_changed = true;
283 } else {
284 if (online_links_.erase(msg->ifi_index))
285 *link_changed = true;
286 }
287 } break;
288 case RTM_DELLINK: {
289 const struct ifinfomsg* msg =
290 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
291 if (online_links_.erase(msg->ifi_index))
292 *link_changed = true;
293 } break;
213 default: 294 default:
214 break; 295 break;
215 } 296 }
216 } 297 }
217 return changed;
218 } 298 }
219 299
220 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { 300 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
221 DCHECK_EQ(netlink_fd_, fd); 301 DCHECK_EQ(netlink_fd_, fd);
222 if (ReadMessages()) 302 bool address_changed;
223 callback_.Run(); 303 bool link_changed;
304 ReadMessages(&address_changed, &link_changed);
305 if (address_changed)
306 address_callback_.Run();
307 if (link_changed)
308 link_callback_.Run();
224 } 309 }
225 310
226 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} 311 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
227 312
313 void AddressTrackerLinux::CloseSocket() {
314 if (netlink_fd_ >= 0 && HANDLE_EINTR(close(netlink_fd_)) < 0)
315 PLOG(ERROR) << "Could not close NETLINK socket.";
316 netlink_fd_ = -1;
317 }
318
228 } // namespace internal 319 } // namespace internal
229 } // namespace net 320 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698