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

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: 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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 return true; 66 return true;
64 } 67 }
65 68
66 void CloseSocket(int fd) { 69 void CloseSocket(int fd) {
67 if (HANDLE_EINTR(close(fd)) < 0) 70 if (HANDLE_EINTR(close(fd)) < 0)
68 PLOG(ERROR) << "Could not close NETLINK socket."; 71 PLOG(ERROR) << "Could not close NETLINK socket.";
69 } 72 }
70 73
71 } // namespace 74 } // namespace
72 75
73 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& callback) 76 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
74 : callback_(callback), 77 const base::Closure& link_callback)
75 netlink_fd_(-1) { 78 : address_callback_(address_callback),
76 DCHECK(!callback.is_null()); 79 link_callback_(link_callback),
80 netlink_fd_(-1),
81 is_offline_(false),
82 offline_state_initialized_(true /*manual_reset*/, false) {
83 DCHECK(!address_callback.is_null());
84 DCHECK(!link_callback.is_null());
77 } 85 }
78 86
79 AddressTrackerLinux::~AddressTrackerLinux() { 87 AddressTrackerLinux::~AddressTrackerLinux() {
80 if (netlink_fd_ >= 0) 88 if (netlink_fd_ >= 0)
81 CloseSocket(netlink_fd_); 89 CloseSocket(netlink_fd_);
82 } 90 }
83 91
92 void AddressTrackerLinux::GetInitialSettings() {
93 // Request dump of addresses.
94 struct sockaddr_nl peer = {};
95 peer.nl_family = AF_NETLINK;
96
97 struct {
98 struct nlmsghdr header;
99 struct rtgenmsg msg;
100 } request = {};
101
102 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request));
103 request.header.nlmsg_type = RTM_GETADDR;
104 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
105 request.header.nlmsg_pid = getpid();
106 request.msg.rtgen_family = AF_UNSPEC;
107
108 int rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len,
109 0, reinterpret_cast<struct sockaddr*>(&peer),
110 sizeof(peer)));
111 if (rv < 0) {
112 PLOG(ERROR) << "Could not send NETLINK request";
113 CloseSocket(netlink_fd_);
114 netlink_fd_ = -1;
115 return;
116 }
117
118 // Consume pending message to populate the AddressMap, but don't notify.
119 // Sending another request without first reading responses results in EBUSY.
120 bool address_changed;
121 bool link_changed;
122 ReadMessages(&address_changed, &link_changed);
123
124 // Request dump of link state
125 request.header.nlmsg_type = RTM_GETLINK;
126
127 rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0,
128 reinterpret_cast<struct sockaddr*>(&peer),
129 sizeof(peer)));
130 if (rv < 0) {
131 PLOG(ERROR) << "Could not send NETLINK request";
132 CloseSocket(netlink_fd_);
133 netlink_fd_ = -1;
134 return;
135 }
136
137 // Consume pending message to populate links_online_, but don't notify.
138 ReadMessages(&address_changed, &link_changed);
139 }
140
84 void AddressTrackerLinux::Init() { 141 void AddressTrackerLinux::Init() {
85 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 142 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
86 if (sock < 0) { 143 if (sock < 0) {
87 PLOG(ERROR) << "Could not create NETLINK socket"; 144 PLOG(ERROR) << "Could not create NETLINK socket";
88 return; 145 return;
89 } 146 }
90 147
91 // Request notifications. 148 // Request notifications.
92 struct sockaddr_nl addr = {}; 149 struct sockaddr_nl addr = {};
93 addr.nl_family = AF_NETLINK; 150 addr.nl_family = AF_NETLINK;
94 addr.nl_pid = getpid(); 151 addr.nl_pid = getpid();
95 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993 152 // 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; 153 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY |
154 RTMGRP_LINK;
97 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); 155 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
98 if (rv < 0) { 156 if (rv < 0) {
99 PLOG(ERROR) << "Could not bind NETLINK socket"; 157 PLOG(ERROR) << "Could not bind NETLINK socket";
100 CloseSocket(sock); 158 CloseSocket(sock);
101 return; 159 return;
102 } 160 }
161 netlink_fd_ = sock;
103 162
104 // Watch for asynchronous messages. 163 GetInitialSettings();
105 if (SetNonBlocking(sock)) { 164 offline_state_initialized_.Signal();
106 PLOG(ERROR) << "Could not make NETLINK socket non-blocking"; 165
107 CloseSocket(sock); 166 rv = MessageLoopForIO::current()->WatchFileDescriptor(
167 netlink_fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
168 if (rv < 0) {
169 PLOG(ERROR) << "Could not watch NETLINK socket";
170 CloseSocket(netlink_fd_);
171 netlink_fd_ = -1;
szym 2012/11/11 16:21:55 Since you need to do this at least once, I suggest
pauljensen 2012/11/12 16:45:21 Done.
108 return; 172 return;
109 } 173 }
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.
120 struct sockaddr_nl peer = {};
121 peer.nl_family = AF_NETLINK;
122
123 struct {
124 struct nlmsghdr header;
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 } 174 }
148 175
149 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { 176 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
150 base::AutoLock lock(lock_); 177 base::AutoLock lock(address_map_lock_);
151 return map_; 178 return address_map_;
152 } 179 }
153 180
154 bool AddressTrackerLinux::ReadMessages() { 181 NetworkChangeNotifier::ConnectionType
182 AddressTrackerLinux::GetCurrentConnectionType() {
183 // http://crbug.com/125097
szym 2012/11/11 16:21:55 Is it still needed? Can't we return offline until
pauljensen 2012/11/12 16:45:21 I'm hesitant to return offline when uninitialized;
184 base::ThreadRestrictions::ScopedAllowWait allow_wait;
185 offline_state_initialized_.Wait();
szym 2012/11/11 16:21:55 I'm concerned that you Wait after locking. I think
pauljensen 2012/11/12 16:45:21 I'm confused; your two sentences contradict each o
186 base::AutoLock lock(is_offline_lock_);
187 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
szym 2012/11/11 16:21:55 Could you find the bug for droger and link it here
pauljensen 2012/11/12 16:45:21 I don't think there is one. I filed one.
188 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
189 NetworkChangeNotifier::CONNECTION_UNKNOWN;
190 }
191
192 void AddressTrackerLinux::ReadMessages(bool* address_changed,
193 bool* link_changed) {
194 *address_changed = false;
195 *link_changed = false;
155 char buffer[4096]; 196 char buffer[4096];
156 bool changed = false; 197 bool first_loop = true;
157 for (;;) { 198 for (;;) {
158 int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 0)); 199 int rv = HANDLE_EINTR(recv(netlink_fd_,
200 buffer,
201 sizeof(buffer),
202 // Block the first time through loop.
szym 2012/11/11 16:21:55 If we are ok with returning offline initially, you
203 first_loop ? 0 : MSG_DONTWAIT));
204 first_loop = false;
159 if (rv == 0) { 205 if (rv == 0) {
160 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; 206 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
161 return false; 207 return;
162 } 208 }
163 if (rv < 0) { 209 if (rv < 0) {
164 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 210 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
165 break; 211 break;
166 PLOG(ERROR) << "Failed to recv from netlink socket"; 212 PLOG(ERROR) << "Failed to recv from netlink socket";
167 return false; 213 return;
168 } 214 }
169 changed |= HandleMessage(buffer, rv); 215 HandleMessage(buffer, rv, address_changed, link_changed);
170 }; 216 };
171 return changed; 217 if (*link_changed) {
218 base::AutoLock lock(is_offline_lock_);
219 is_offline_ = online_links_.empty();
220 }
172 } 221 }
173 222
174 bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) { 223 void AddressTrackerLinux::HandleMessage(const char* buffer,
224 size_t length,
225 bool* address_changed,
226 bool* link_changed) {
175 DCHECK(buffer); 227 DCHECK(buffer);
176 bool changed = false;
177 for (const struct nlmsghdr* header = 228 for (const struct nlmsghdr* header =
178 reinterpret_cast<const struct nlmsghdr*>(buffer); 229 reinterpret_cast<const struct nlmsghdr*>(buffer);
179 NLMSG_OK(header, length); 230 NLMSG_OK(header, length);
180 header = NLMSG_NEXT(header, length)) { 231 header = NLMSG_NEXT(header, length)) {
181 switch (header->nlmsg_type) { 232 switch (header->nlmsg_type) {
182 case NLMSG_DONE: 233 case NLMSG_DONE:
183 return changed; 234 return;
184 case NLMSG_ERROR: 235 case NLMSG_ERROR: {
185 LOG(ERROR) << "Unexpected netlink error."; 236 const struct nlmsgerr* msg =
186 return changed; 237 reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header));
238 LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
239 } return;
187 case RTM_NEWADDR: { 240 case RTM_NEWADDR: {
188 IPAddressNumber address; 241 IPAddressNumber address;
189 if (GetAddress(header, &address)) { 242 if (GetAddress(header, &address)) {
190 base::AutoLock lock(lock_); 243 base::AutoLock lock(address_map_lock_);
191 const struct ifaddrmsg* msg = 244 const struct ifaddrmsg* msg =
192 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); 245 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
193 // Only indicate change if the address is new or ifaddrmsg info has 246 // Only indicate change if the address is new or ifaddrmsg info has
194 // changed. 247 // changed.
195 AddressMap::iterator it = map_.find(address); 248 AddressMap::iterator it = address_map_.find(address);
196 if (it == map_.end()) { 249 if (it == address_map_.end()) {
197 map_.insert(it, std::make_pair(address, *msg)); 250 address_map_.insert(it, std::make_pair(address, *msg));
198 changed = true; 251 *address_changed = true;
199 } else if (memcmp(&it->second, msg, sizeof(*msg))) { 252 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
200 it->second = *msg; 253 it->second = *msg;
201 changed = true; 254 *address_changed = true;
202 } 255 }
203 } 256 }
204 } break; 257 } break;
205 case RTM_DELADDR: { 258 case RTM_DELADDR: {
206 IPAddressNumber address; 259 IPAddressNumber address;
207 if (GetAddress(header, &address)) { 260 if (GetAddress(header, &address)) {
208 base::AutoLock lock(lock_); 261 base::AutoLock lock(address_map_lock_);
209 if (map_.erase(address)) 262 if (address_map_.erase(address))
210 changed = true; 263 *address_changed = true;
211 } 264 }
212 } break; 265 } break;
266 case RTM_NEWLINK: {
267 const struct ifinfomsg* msg =
268 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
269 if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
270 (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
271 if (online_links_.insert(msg->ifi_index).second)
272 *link_changed = true;
273 } else {
274 if (online_links_.erase(msg->ifi_index))
275 *link_changed = true;
276 }
277 } break;
278 case RTM_DELLINK: {
279 const struct ifinfomsg* msg =
280 reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header));
281 if (online_links_.erase(msg->ifi_index))
282 *link_changed = true;
283 } break;
213 default: 284 default:
214 break; 285 break;
215 } 286 }
216 } 287 }
217 return changed;
218 } 288 }
219 289
220 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { 290 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
221 DCHECK_EQ(netlink_fd_, fd); 291 DCHECK_EQ(netlink_fd_, fd);
222 if (ReadMessages()) 292 bool address_changed;
223 callback_.Run(); 293 bool link_changed;
294 ReadMessages(&address_changed, &link_changed);
295 if (address_changed)
296 address_callback_.Run();
297 if (link_changed)
298 link_callback_.Run();
224 } 299 }
225 300
226 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} 301 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
227 302
228 } // namespace internal 303 } // namespace internal
229 } // namespace net 304 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698