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

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 test for ignored attribute. 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
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), netlink_fd_(-1) {
75 DCHECK(!callback.is_null());
76 }
77
78 AddressTrackerLinux::~AddressTrackerLinux() {
79 if (netlink_fd_ >= 0)
80 CloseSocket(netlink_fd_);
81 }
82
83 void AddressTrackerLinux::Init() {
84 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
85 if (sock < 0) {
86 PLOG(ERROR) << "Could not create NETLINK socket";
87 return;
88 }
89
90 // Request notifications.
91 struct sockaddr_nl addr = {};
92 addr.nl_family = AF_NETLINK;
93 addr.nl_pid = getpid();
94 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993
95 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY;
96 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
97 if (rv < 0) {
98 PLOG(ERROR) << "Could not bind NETLINK socket";
99 CloseSocket(sock);
100 return;
101 }
102
103 // Watch for asynchronous messages.
104 if (SetNonBlocking(sock)) {
105 PLOG(ERROR) << "Could not make NETLINK socket non-blocking";
106 CloseSocket(sock);
107 return;
108 }
109
110 rv = MessageLoopForIO::current()->WatchFileDescriptor(
111 sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this);
112 if (rv < 0) {
113 PLOG(ERROR) << "Could not watch NETLINK socket";
114 CloseSocket(sock);
115 return;
116 }
117
118 // Request dump of addresses.
119 struct sockaddr_nl peer = {};
120 peer.nl_family = AF_NETLINK;
121
122 struct {
123 struct nlmsghdr header;
124 struct rtgenmsg msg;
125 } request = {};
126
127 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
128 request.header.nlmsg_type = RTM_GETADDR;
129 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
130 request.header.nlmsg_pid = getpid();
131 request.msg.rtgen_family = AF_UNSPEC;
132
133 rv = HANDLE_EINTR(sendto(sock, &request, request.header.nlmsg_len, 0,
134 reinterpret_cast<struct sockaddr*>(&peer),
135 sizeof(peer)));
136 if (rv < 0) {
137 PLOG(ERROR) << "Could not send NETLINK request";
138 CloseSocket(sock);
139 return;
140 }
141
142 netlink_fd_ = sock;
143 }
144
145 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetMap() const {
146 base::AutoLock lock(lock_);
147 return map_;
148 }
149
150 bool AddressTrackerLinux::ReadMessages() {
151 char buffer[4096];
152 bool changed = false;
153 for (;;) {
154 int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 0));
155 if (rv == 0) {
156 LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
157 return false;
158 }
159 if (rv < 0) {
160 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
161 break;
162 PLOG(ERROR) << "Failed to recv from netlink socket";
163 return false;
164 }
165 changed |= HandleMessage(buffer, rv);
166 };
167 return changed;
168 }
169
170 bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) {
171 DCHECK(buffer);
172 bool changed = false;
173 for (const struct nlmsghdr* header =
174 reinterpret_cast<const struct nlmsghdr*>(buffer);
175 NLMSG_OK(header, length);
176 header = NLMSG_NEXT(header, length)) {
177 switch (header->nlmsg_type) {
178 case NLMSG_DONE:
179 return changed;
180 case NLMSG_ERROR:
181 LOG(ERROR) << "Unexpected netlink error.";
182 return changed;
183 case RTM_NEWADDR: {
184 IPAddressNumber address;
185 if (GetAddress(header, &address)) {
186 base::AutoLock lock(lock_);
187 const struct ifaddrmsg* msg =
188 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header));
189 // Only indicate change if the address is new or ifaddrmsg info has
190 // changed.
191 AddressMap::iterator it = map_.find(address);
192 if (it == map_.end()) {
193 map_.insert(it, std::make_pair(address, *msg));
194 changed = true;
195 } else if (memcmp(&it->second, msg, sizeof(*msg))) {
196 it->second = *msg;
197 changed = true;
198 }
199 }
200 } break;
201 case RTM_DELADDR: {
202 IPAddressNumber address;
203 if (GetAddress(header, &address)) {
204 base::AutoLock lock(lock_);
205 if (map_.erase(address))
206 changed = true;
207 }
208 } break;
209 default:
210 break;
211 }
212 }
213 return changed;
214 }
215
216 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) {
217 DCHECK_EQ(netlink_fd_, fd);
218 if (ReadMessages())
219 callback_.Run();
220 }
221
222 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {}
223
224 } // namespace internal
225 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698