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

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

Issue 10689015: [net] Adds AddressTrackerLinux which keeps track of interface addresses using rtnetlink. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added NET_EXPORT_PRIVATE for tests. Created 8 years, 5 months 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "net/base/address_tracker_linux.h"
6
7 #include <errno.h>
8
9 #include "base/eintr_wrapper.h"
10 #include "base/logging.h"
11 #include "net/base/network_change_notifier_linux.h"
12
13 namespace net {
14 namespace internal {
15
16 namespace {
17
18 // Retrieves address from NETLINK address message.
19 bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) {
20 const struct ifaddrmsg* msg =
21 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
22 size_t address_length = 0;
23 switch (msg->ifa_family) {
24 case AF_INET:
25 address_length = kIPv4AddressSize;
26 break;
27 case AF_INET6:
28 address_length = kIPv6AddressSize;
29 break;
30 default:
31 // Unknown family.
32 return false;
33 }
34 // Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
35 // getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
36 // NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
37 // have the IFA_LOCAL attribute.
38 unsigned char* address = NULL;
39 unsigned char* local = NULL;
40 size_t length = IFA_PAYLOAD(header);
41 for (const struct rtattr* attr =
42 reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
43 RTA_OK(attr, length);
44 attr = RTA_NEXT(attr, length)) {
45 switch (attr->rta_type) {
46 case IFA_ADDRESS:
47 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
48 address = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
49 break;
50 case IFA_LOCAL:
51 DCHECK_GE(RTA_PAYLOAD(attr), address_length);
52 local = reinterpret_cast<unsigned char*>(RTA_DATA(attr));
53 break;
54 default:
55 break;
56 }
57 }
58 if (local)
59 address = local;
60 if (!address)
61 return false;
62 out->assign(address, address + address_length);
63 return true;
64 }
65
66 void CloseSocket(int fd) {
67 if (HANDLE_EINTR(close(fd)) < 0)
68 PLOG(ERROR) << "Could not close NETLINK socket.";
69 }
70
71 } // namespace
72
73 AddressTrackerLinux::AddressTrackerLinux(const base::Closure& callback)
74 : callback_(callback),
75 netlink_fd_(-1) {
76 DCHECK(!callback.is_null());
77 }
78
79 AddressTrackerLinux::~AddressTrackerLinux() {
80 if (netlink_fd_ >= 0)
81 CloseSocket(netlink_fd_);
82 }
83
84 void AddressTrackerLinux::Init() {
85 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
86 if (sock < 0) {
87 PLOG(ERROR) << "Could not create NETLINK socket";
88 return;
89 }
90
91 // Request notifications.
92 struct sockaddr_nl addr = {};
93 addr.nl_family = AF_NETLINK;
94 addr.nl_pid = getpid();
95 // 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;
97 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
98 if (rv < 0) {
99 PLOG(ERROR) << "Could not bind NETLINK socket";
100 CloseSocket(sock);
101 return;
102 }
103
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.
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
146 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
147 base::AutoLock lock(lock_);
148 return map_;
149 }
150
151 bool AddressTrackerLinux::ReadMessages() {
152 char buffer[4096];
153 bool changed = false;
154 for (;;) {
155 int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 0));
156 if (rv == 0) {
157 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
158 return false;
159 }
160 if (rv < 0) {
161 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
162 break;
163 PLOG(ERROR) << "Failed to recv from netlink socket";
164 return false;
165 }
166 changed |= HandleMessage(buffer, rv);
167 };
168 return changed;
169 }
170
171 bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) {
172 DCHECK(buffer);
173 bool changed = false;
174 for (const struct nlmsghdr* header =
175 reinterpret_cast<const struct nlmsghdr*>(buffer);
176 NLMSG_OK(header, length);
177 header = NLMSG_NEXT(header, length)) {
178 switch (header->nlmsg_type) {
179 case NLMSG_DONE:
180 return changed;
181 case NLMSG_ERROR:
182 LOG(ERROR) << "Unexpected netlink error.";
183 return changed;
184 case RTM_NEWADDR: {
185 IPAddressNumber address;
186 if (GetAddress(header, &address)) {
187 base::AutoLock lock(lock_);
188 const struct ifaddrmsg* msg =
189 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
190 // Only indicate change if the address is new or ifaddrmsg info has
191 // changed.
192 AddressMap::iterator it = map_.find(address);
193 if (it == map_.end()) {
194 map_.insert(it, std::make_pair(address, *msg));
195 changed = true;
196 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
197 it->second = *msg;
198 changed = true;
199 }
200 }
201 } break;
202 case RTM_DELADDR: {
203 IPAddressNumber address;
204 if (GetAddress(header, &address)) {
205 base::AutoLock lock(lock_);
206 if (map_.erase(address))
207 changed = true;
208 }
209 } break;
210 default:
211 break;
212 }
213 }
214 return changed;
215 }
216
217 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
218 DCHECK_EQ(netlink_fd_, fd);
219 if (ReadMessages())
220 callback_.Run();
221 }
222
223 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
224
225 } // namespace internal
226 } // 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