OLD | NEW |
---|---|
(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 size_t length = IFA_PAYLOAD(header); | |
35 const struct rtattr* attr = reinterpret_cast<struct rtattr*>(IFA_RTA(msg)); | |
36 unsigned char* address = NULL; | |
37 unsigned char* local = NULL; | |
38 for (; RTA_OK(attr, length); attr = RTA_NEXT(attr, length)) { | |
39 switch (attr->rta_type) { | |
40 case IFA_ADDRESS: | |
41 local = reinterpret_cast<unsigned char*>(RTA_DATA(attr)); | |
42 break; | |
43 case IFA_LOCAL: | |
44 address = reinterpret_cast<unsigned char*>(RTA_DATA(attr)); | |
45 break; | |
46 default: | |
47 break; | |
48 } | |
49 } | |
50 if (local) | |
51 address = local; | |
52 if (!address) | |
53 return false; | |
54 out->assign(address, address + address_length); | |
55 return true; | |
56 } | |
57 | |
58 void CloseSocket(int fd) { | |
59 if (HANDLE_EINTR(close(fd)) < 0) | |
60 PLOG(ERROR) << "Could not close NETLINK socket."; | |
61 } | |
62 | |
63 } // namespace | |
64 | |
65 AddressTrackerLinux::AddressTrackerLinux() : netlink_fd_(-1) {} | |
66 | |
67 AddressTrackerLinux::~AddressTrackerLinux() { | |
68 if (netlink_fd_ >= 0) | |
69 CloseSocket(netlink_fd_); | |
70 } | |
71 | |
72 void AddressTrackerLinux::Init() { | |
73 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
74 if (sock < 0) { | |
75 PLOG(ERROR) << "Could not create NETLINK socket"; | |
76 return; | |
77 } | |
78 | |
79 // Request notifications. | |
80 struct sockaddr_nl addr = {}; | |
81 addr.nl_family = AF_NETLINK; | |
82 addr.nl_pid = getpid(); | |
83 // TODO(szym): Track RTMGRP_LINK as well for ifi_type | |
84 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY; | |
85 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); | |
86 if (rv < 0) { | |
87 PLOG(ERROR) << "Could not bind NETLINK socket"; | |
88 CloseSocket(sock); | |
89 return; | |
90 } | |
91 | |
92 // Request dump of addresses. | |
93 struct sockaddr_nl peer = {}; | |
94 peer.nl_family = AF_NETLINK; | |
95 | |
96 struct { | |
97 struct nlmsghdr header; | |
98 struct rtgenmsg msg; | |
99 } request = {}; | |
100 | |
101 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg)); | |
102 request.header.nlmsg_type = RTM_GETADDR; | |
103 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; | |
104 request.header.nlmsg_pid = getpid(); | |
105 request.msg.rtgen_family = AF_UNSPEC; | |
106 | |
107 rv = HANDLE_EINTR(sendto(sock, &request, request.header.nlmsg_len, 0, | |
108 reinterpret_cast<struct sockaddr*>(&peer), | |
109 sizeof(peer))); | |
110 if (rv < 0) { | |
111 PLOG(ERROR) << "Could not send NETLINK request"; | |
112 CloseSocket(sock); | |
113 return; | |
114 } | |
115 | |
116 // Watch for asynchronous messages. | |
117 if (SetNonBlocking(sock)) { | |
118 PLOG(ERROR) << "Could not make NETLINK socket non-blocking"; | |
119 CloseSocket(sock); | |
120 return; | |
121 } | |
122 | |
123 rv = MessageLoopForIO::current()->WatchFileDescriptor( | |
124 sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this); | |
125 if (rv < 0) { | |
126 PLOG(ERROR) << "Could not watch NETLINK socket"; | |
127 CloseSocket(sock); | |
128 return; | |
129 } | |
130 | |
131 netlink_fd_ = sock; | |
132 ReadMessages(); | |
vandebo (ex-Chrome)
2012/06/29 17:52:03
Do you need to call ReadMessages here? Won't watc
szym
2012/06/29 18:34:52
The intention was to read all messages in response
| |
133 } | |
134 | |
135 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetMap() const { | |
136 base::AutoLock lock(lock_); | |
137 return map_; | |
138 } | |
139 | |
140 bool AddressTrackerLinux::ReadMessages() { | |
141 char buf[4096]; | |
142 bool changed = false; | |
143 for (;;) { | |
144 int rv = HANDLE_EINTR(recv(netlink_fd_, buf, sizeof(buf), 0)); | |
145 if (rv == 0) { | |
146 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; | |
147 return false; | |
148 } | |
149 if (rv < 0) { | |
150 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
151 break; | |
152 PLOG(ERROR) << "Failed to recv from netlink socket"; | |
153 return false; | |
154 } | |
155 changed |= HandleMessage(buf, rv); | |
156 }; | |
157 return changed; | |
158 } | |
159 | |
160 bool AddressTrackerLinux::HandleMessage(char* buf, size_t len) { | |
161 DCHECK(buf); | |
162 bool changed = false; | |
163 const struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buf); | |
164 for (; NLMSG_OK(header, len); header = NLMSG_NEXT(header, len)) { | |
165 switch (header->nlmsg_type) { | |
vandebo (ex-Chrome)
2012/06/29 17:52:03
So this logic is no worse than what we had before,
szym
2012/06/29 18:34:52
The goal was to filter out RTM_NEWADDR announcemen
vandebo (ex-Chrome)
2012/06/29 21:01:18
It's true that 100690 implies that only the time s
szym
2012/06/29 21:09:06
We must not simply ignore such a change. In partic
| |
166 case NLMSG_DONE: | |
167 return changed; | |
168 case NLMSG_ERROR: | |
169 LOG(ERROR) << "Unexpected netlink error."; | |
170 return changed; | |
171 case RTM_NEWADDR: { | |
172 IPAddressNumber address; | |
173 if (GetAddress(header, &address)) { | |
174 base::AutoLock lock(lock_); | |
175 const struct ifaddrmsg* msg = | |
176 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); | |
177 AddressMap::iterator it = map_.find(address); | |
178 if (it == map_.end()) { | |
179 map_.insert(it, std::make_pair(address, *msg)); | |
180 changed = true; | |
181 } else if (memcmp(&it->second, msg, sizeof(*msg))) { | |
182 it->second = *msg; | |
183 changed = true; | |
184 } | |
185 } | |
186 } break; | |
187 case RTM_DELADDR: { | |
188 IPAddressNumber address; | |
189 if (GetAddress(header, &address)) { | |
190 base::AutoLock lock(lock_); | |
191 if (map_.erase(address)) | |
192 changed = true; | |
193 } | |
194 } break; | |
195 case RTM_NEWLINK: | |
196 case RTM_DELLINK: | |
197 // TODO(szym): handle for ifi_type. | |
198 break; | |
199 default: | |
200 break; | |
201 } | |
202 } | |
203 return changed; | |
204 } | |
205 | |
206 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { | |
207 if (ReadMessages()) | |
208 NetworkChangeNotifierLinux::NotifyObserversOfIPAddressChange(); | |
209 } | |
210 | |
211 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} | |
212 | |
213 } // namespace internal | |
214 } // namespace net | |
OLD | NEW |