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; | |
willchan no longer on Chromium
2012/06/29 01:53:41
be consistent about type* vs type *. chromium pref
| |
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 } // namespace | |
59 | |
60 AddressTrackerLinux::AddressTrackerLinux() : netlink_fd_(-1) {} | |
61 | |
62 AddressTrackerLinux::~AddressTrackerLinux() { | |
63 if (netlink_fd_ >= 0) { | |
64 watcher_.StopWatchingFileDescriptor(); | |
65 close(netlink_fd_); | |
willchan no longer on Chromium
2012/06/29 01:53:41
HANDLE_EINTR()
| |
66 } | |
67 } | |
68 | |
69 void AddressTrackerLinux::Init() { | |
70 int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
71 if (sock < 0) { | |
72 PLOG(ERROR) << "Could not create NETLINK socket"; | |
73 return; | |
74 } | |
75 | |
76 // Request notifications. | |
77 struct sockaddr_nl addr = {}; | |
78 addr.nl_family = AF_NETLINK; | |
79 addr.nl_pid = getpid(); | |
80 // TODO(szym): Track RTMGRP_LINK as well for ifi_type | |
81 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY; | |
82 int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); | |
83 if (rv < 0) { | |
84 PLOG(ERROR) << "Could not bind NETLINK socket"; | |
85 close(sock); | |
86 return; | |
87 } | |
88 | |
89 // Request dump of addresses. | |
90 struct sockaddr_nl peer = {}; | |
91 peer.nl_family = AF_NETLINK; | |
92 | |
93 struct { | |
94 struct nlmsghdr header; | |
95 struct rtgenmsg msg; | |
96 } request = {}; | |
97 | |
98 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg)); | |
99 request.header.nlmsg_type = RTM_GETADDR; | |
100 request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; | |
101 request.header.nlmsg_pid = getpid(); | |
102 request.msg.rtgen_family = AF_UNSPEC; | |
103 | |
104 rv = HANDLE_EINTR(sendto(sock, &request, request.header.nlmsg_len, 0, | |
105 reinterpret_cast<struct sockaddr*>(&peer), | |
106 sizeof(peer))); | |
107 if (rv < 0) { | |
108 PLOG(ERROR) << "Could not send NETLINK request"; | |
109 close(sock); | |
110 return; | |
111 } | |
112 | |
113 // Watch for asynchronous messages. | |
114 if (SetNonBlocking(sock)) { | |
115 PLOG(ERROR) << "Could not make NETLINK socket non-blocking"; | |
116 close(sock); | |
117 return; | |
118 } | |
119 | |
120 rv = MessageLoopForIO::current()->WatchFileDescriptor( | |
121 sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this); | |
122 if (rv < 0) { | |
123 PLOG(ERROR) << "Could not watch NETLINK socket"; | |
124 close(sock); | |
125 return; | |
126 } | |
127 | |
128 netlink_fd_ = sock; | |
129 ReadMessages(); | |
130 } | |
131 | |
132 AddressTrackerLinux::AddressMap AddressTrackerLinux::GetMap() const { | |
133 base::AutoLock lock(lock_); | |
134 return map_; | |
135 } | |
136 | |
137 bool AddressTrackerLinux::ReadMessages() { | |
138 char buf[4096]; | |
139 bool changed = false; | |
140 for (;;) { | |
141 int rv = HANDLE_EINTR(recv(netlink_fd_, buf, sizeof(buf), 0)); | |
142 if (rv == 0) { | |
143 LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; | |
144 return false; | |
145 } | |
146 if (rv < 0) { | |
147 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
148 break; | |
149 PLOG(ERROR) << "Failed to recv from netlink socket"; | |
150 return false; | |
151 } | |
152 changed |= HandleMessage(buf, rv); | |
153 }; | |
154 return changed; | |
155 } | |
156 | |
157 bool AddressTrackerLinux::HandleMessage(char* buf, size_t len) { | |
158 DCHECK(buf); | |
159 bool changed = false; | |
160 const struct nlmsghdr* header = reinterpret_cast<struct nlmsghdr*>(buf); | |
161 for (; NLMSG_OK(header, len); header = NLMSG_NEXT(header, len)) { | |
162 switch (header->nlmsg_type) { | |
163 case NLMSG_DONE: | |
164 return changed; | |
165 case NLMSG_ERROR: | |
166 LOG(ERROR) << "Unexpected netlink error."; | |
167 return changed; | |
168 case RTM_NEWADDR: { | |
169 IPAddressNumber address; | |
170 if (GetAddress(header, &address)) { | |
171 base::AutoLock lock(lock_); | |
172 const struct ifaddrmsg* msg = | |
173 reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); | |
174 AddressMap::iterator it = map_.find(address); | |
175 if (it == map_.end()) { | |
176 map_.insert(it, std::make_pair(address, *msg)); | |
177 changed = true; | |
178 } else if (memcmp(&it->second, msg, sizeof(*msg))) { | |
179 it->second = *msg; | |
180 changed = true; | |
181 } | |
182 } | |
183 } break; | |
184 case RTM_DELADDR: { | |
185 IPAddressNumber address; | |
186 if (GetAddress(header, &address)) { | |
187 base::AutoLock lock(lock_); | |
188 if (map_.erase(address)) | |
189 changed = true; | |
190 } | |
191 } break; | |
192 case RTM_NEWLINK: | |
193 case RTM_DELLINK: | |
194 // TODO(szym): handle for ifi_type. | |
195 break; | |
196 default: | |
197 break; | |
198 } | |
199 } | |
200 return changed; | |
201 } | |
202 | |
203 void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { | |
204 if (ReadMessages()) | |
205 NetworkChangeNotifierLinux::NotifyObserversOfIPAddressChange(); | |
206 } | |
207 | |
208 void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} | |
209 | |
210 } // namespace internal | |
211 } // namespace net | |
OLD | NEW |