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 unsigned char* address = NULL; | |
35 unsigned char* local = NULL; | |
36 const struct rtattr* attr = reinterpret_cast<struct rtattr*>(IFA_RTA(msg)); | |
37 size_t length = IFA_PAYLOAD(header); | |
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(const base::Closure& callback) | |
66 : callback_(callback), netlink_fd_(-1) { | |
vandebo (ex-Chrome)
2012/06/29 21:01:18
nit: style guide says that if they don't all fit o
vandebo (ex-Chrome)
2012/06/29 22:24:29
You seem to have missed this comment.
vandebo (ex-Chrome)
2012/07/12 18:34:26
Please fix this code to adhere to the style guide.
szym
2012/07/12 18:55:47
Done.
| |
67 DCHECK(!callback.is_null()); | |
68 } | |
69 | |
70 AddressTrackerLinux::~AddressTrackerLinux() { | |
71 if (netlink_fd_ >= 0) | |
72 CloseSocket(netlink_fd_); | |
73 } | |
74 | |
75 void AddressTrackerLinux::Init() { | |
76 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
77 if (sock < 0) { | |
78 PLOG(ERROR) << "Could not create NETLINK socket"; | |
79 return; | |
80 } | |
81 | |
82 // Request notifications. | |
83 struct sockaddr_nl addr = {}; | |
84 addr.nl_family = AF_NETLINK; | |
85 addr.nl_pid = getpid(); | |
86 // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993 | |
87 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY; | |
88 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); | |
89 if (rv < 0) { | |
90 PLOG(ERROR) << "Could not bind NETLINK socket"; | |
91 CloseSocket(sock); | |
92 return; | |
93 } | |
94 | |
95 // Watch for asynchronous messages. | |
96 if (SetNonBlocking(sock)) { | |
97 PLOG(ERROR) << "Could not make NETLINK socket non-blocking"; | |
98 CloseSocket(sock); | |
99 return; | |
100 } | |
101 | |
102 rv = MessageLoopForIO::current()->WatchFileDescriptor( | |
103 sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this); | |
104 if (rv < 0) { | |
105 PLOG(ERROR) << "Could not watch NETLINK socket"; | |
106 CloseSocket(sock); | |
107 return; | |
108 } | |
109 | |
110 // Request dump of addresses. | |
111 struct sockaddr_nl peer = {}; | |
112 peer.nl_family = AF_NETLINK; | |
113 | |
114 struct { | |
115 struct nlmsghdr header; | |
116 struct rtgenmsg msg; | |
117 } request = {}; | |
118 | |
119 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg)); | |
120 request.header.nlmsg_type = RTM_GETADDR; | |
121 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; | |
122 request.header.nlmsg_pid = getpid(); | |
123 request.msg.rtgen_family = AF_UNSPEC; | |
124 | |
125 rv = HANDLE_EINTR(sendto(sock, &request, request.header.nlmsg_len, 0, | |
126 reinterpret_cast<struct sockaddr*>(&peer), | |
127 sizeof(peer))); | |
128 if (rv < 0) { | |
129 PLOG(ERROR) << "Could not send NETLINK request"; | |
130 CloseSocket(sock); | |
131 return; | |
132 } | |
133 | |
134 netlink_fd_ = sock; | |
135 } | |
136 | |
137 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetMap() const { | |
138 base::AutoLock lock(lock_); | |
139 return map_; | |
140 } | |
141 | |
142 bool AddressTrackerLinux::ReadMessages() { | |
143 char buf[4096]; | |
144 bool changed = false; | |
145 for (;;) { | |
146 int rv = HANDLE_EINTR(recv(netlink_fd_, buf, sizeof(buf), 0)); | |
147 if (rv == 0) { | |
148 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; | |
149 return false; | |
150 } | |
151 if (rv < 0) { | |
152 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
153 break; | |
154 PLOG(ERROR) << "Failed to recv from netlink socket"; | |
155 return false; | |
156 } | |
157 changed |= HandleMessage(buf, rv); | |
158 }; | |
159 return changed; | |
160 } | |
161 | |
162 bool AddressTrackerLinux::HandleMessage(char* buf, size_t len) { | |
vandebo (ex-Chrome)
2012/06/29 21:01:18
You should be able to write a unit test for this m
szym
2012/07/12 18:55:47
Done.
| |
163 DCHECK(buf); | |
164 bool changed = false; | |
165 for (const struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buf); | |
166 NLMSG_OK(header, len); header = NLMSG_NEXT(header, len)) { | |
167 switch (header->nlmsg_type) { | |
168 case NLMSG_DONE: | |
169 return changed; | |
170 case NLMSG_ERROR: | |
171 LOG(ERROR) << "Unexpected netlink error."; | |
172 return changed; | |
173 case RTM_NEWADDR: { | |
174 IPAddressNumber address; | |
175 if (GetAddress(header, &address)) { | |
176 base::AutoLock lock(lock_); | |
177 const struct ifaddrmsg* msg = | |
178 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); | |
179 // Only indicate change if the address is new or ifaddrmsg info has | |
180 // changed. | |
181 AddressMap::iterator it = map_.find(address); | |
182 if (it == map_.end()) { | |
183 map_.insert(it, std::make_pair(address, *msg)); | |
184 changed = true; | |
185 } else if (memcmp(&it->second, msg, sizeof(*msg))) { | |
186 it->second = *msg; | |
187 changed = true; | |
188 } | |
189 } | |
190 } break; | |
191 case RTM_DELADDR: { | |
192 IPAddressNumber address; | |
193 if (GetAddress(header, &address)) { | |
194 base::AutoLock lock(lock_); | |
195 if (map_.erase(address)) | |
196 changed = true; | |
197 } | |
198 } break; | |
199 default: | |
200 break; | |
201 } | |
202 } | |
203 return changed; | |
204 } | |
205 | |
206 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { | |
207 if (ReadMessages()) | |
208 callback_.Run(); | |
209 } | |
210 | |
211 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} | |
212 | |
213 } // namespace internal | |
214 } // namespace net | |
OLD | NEW |