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

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

Powered by Google App Engine
This is Rietveld 408576698