1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "resolv"
18
19 #include "Dns64Configuration.h"
20
21 #include <android-base/logging.h>
22 #include <netdb.h>
23 #include <netdutils/BackoffSequence.h>
24 #include <netdutils/DumpWriter.h>
25 #include <netdutils/InternetAddresses.h>
26 #include <netdutils/ThreadUtil.h>
27 #include <thread>
28 #include <utility>
29
30 #include <arpa/inet.h>
31
32 #include "DnsResolver.h"
33 #include "getaddrinfo.h"
34 #include "netd_resolv/resolv.h"
35 #include "stats.pb.h"
36
37 namespace android {
38
39 using netdutils::DumpWriter;
40 using netdutils::IPAddress;
41 using netdutils::IPPrefix;
42 using netdutils::ScopedAddrinfo;
43 using netdutils::setThreadName;
44
45 namespace net {
46
47 const char Dns64Configuration::kIPv4OnlyHost[] = "ipv4only.arpa.";
48 const char Dns64Configuration::kIPv4Literal1[] = "192.0.0.170";
49 const char Dns64Configuration::kIPv4Literal2[] = "192.0.0.171";
50
startPrefixDiscovery(unsigned netId)51 void Dns64Configuration::startPrefixDiscovery(unsigned netId) {
52 std::lock_guard guard(mMutex);
53
54 // TODO: Keep previous prefix for a while
55 // Currently, we remove current prefix, if any, before starting a prefix discovery.
56 // This causes that Netd and framework temporarily forgets DNS64 prefix even the prefix may be
57 // discovered in a short time.
58 removeDns64Config(netId);
59
60 Dns64Config cfg(getNextId(), netId);
61 // Emplace a copy of |cfg| in the map.
62 mDns64Configs.emplace(std::make_pair(netId, cfg));
63
64 const std::shared_ptr<Dns64Configuration> thiz = shared_from_this();
65 // Note that capturing |cfg| in this lambda creates a copy.
66 std::thread discovery_thread([thiz, cfg, netId] {
67 setThreadName(fmt::format("Nat64Pfx_{}", netId));
68
69 // Make a mutable copy rather than mark the whole lambda mutable.
70 // No particular reason.
71 Dns64Config evalCfg(cfg);
72
73 auto backoff = netdutils::BackoffSequence<>::Builder()
74 .withInitialRetransmissionTime(std::chrono::seconds(1))
75 .withMaximumRetransmissionTime(std::chrono::seconds(3600))
76 .build();
77
78 while (true) {
79 if (!thiz->shouldContinueDiscovery(evalCfg)) break;
80
81 android_net_context netcontext{};
82 thiz->mGetNetworkContextCallback(evalCfg.netId, 0, &netcontext);
83
84 // Prefix discovery must bypass private DNS because in strict mode
85 // the server generally won't know the NAT64 prefix.
86 netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
87 if (doRfc7050PrefixDiscovery(netcontext, &evalCfg)) {
88 thiz->recordDns64Config(evalCfg);
89 break;
90 }
91
92 if (!thiz->shouldContinueDiscovery(evalCfg)) break;
93
94 if (!backoff.hasNextTimeout()) break;
95 {
96 std::unique_lock<std::mutex> cvGuard(thiz->mMutex);
97 // TODO: Consider some chrono math, combined with wait_until()
98 // perhaps, to prevent early re-resolves from the removal of
99 // other netids with IPv6-only nameservers.
100 thiz->mCv.wait_for(cvGuard, backoff.getNextTimeout());
101 }
102 }
103 });
104 discovery_thread.detach();
105 }
106
stopPrefixDiscovery(unsigned netId)107 void Dns64Configuration::stopPrefixDiscovery(unsigned netId) {
108 std::lock_guard guard(mMutex);
109 removeDns64Config(netId);
110 mCv.notify_all();
111 }
112
getPrefix64Locked(unsigned netId) const113 IPPrefix Dns64Configuration::getPrefix64Locked(unsigned netId) const REQUIRES(mMutex) {
114 const auto& iter = mDns64Configs.find(netId);
115 if (iter != mDns64Configs.end()) return iter->second.prefix64;
116
117 return IPPrefix{};
118 }
119
getPrefix64(unsigned netId) const120 IPPrefix Dns64Configuration::getPrefix64(unsigned netId) const {
121 std::lock_guard guard(mMutex);
122 return getPrefix64Locked(netId);
123 }
124
dump(DumpWriter & dw,unsigned netId)125 void Dns64Configuration::dump(DumpWriter& dw, unsigned netId) {
126 static const char kLabel[] = "DNS64 config";
127
128 std::lock_guard guard(mMutex);
129
130 const auto& iter = mDns64Configs.find(netId);
131 if (iter == mDns64Configs.end()) {
132 dw.println("%s: none", kLabel);
133 return;
134 }
135
136 const Dns64Config& cfg = iter->second;
137 if (cfg.prefix64.length() == 0) {
138 dw.println("%s: no prefix yet discovered", kLabel);
139 } else {
140 dw.println("%s: %s prefix %s", kLabel, cfg.isFromPrefixDiscovery() ? "discovered" : "set",
141 cfg.prefix64.toString().c_str());
142 }
143 }
144
145 // NOTE: The full RFC 7050 DNS64 discovery process is not implemented here.
146 // Instead, and more simplistic version of the same thing is done, and it
147 // currently assumes the DNS64 prefix is a /96.
doRfc7050PrefixDiscovery(const android_net_context & netcontext,Dns64Config * cfg)148 bool Dns64Configuration::doRfc7050PrefixDiscovery(const android_net_context& netcontext,
149 Dns64Config* cfg) {
150 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId
151 << ") Detecting NAT64 prefix from DNS...";
152
153 const struct addrinfo hints = {
154 .ai_family = AF_INET6,
155 };
156
157 // TODO: Refactor so that netd can get all the regular getaddrinfo handling
158 // that regular apps get. We bypass the UNIX socket connection back to
159 // ourselves, which means we also bypass all the special netcontext flag
160 // handling and the resolver event logging.
161 struct addrinfo* res = nullptr;
162 NetworkDnsEventReported event;
163 const int status =
164 resolv_getaddrinfo(kIPv4OnlyHost, nullptr, &hints, &netcontext, &res, &event);
165 ScopedAddrinfo result(res);
166 if (status != 0) {
167 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId << ") plat_prefix/dns("
168 << kIPv4OnlyHost << ") status = " << status << "/" << gai_strerror(status);
169 return false;
170 }
171
172 // Use only the first result. If other records are present, possibly
173 // with differing DNS64 prefixes they are ignored. Note that this is a
174 // violation of https://tools.ietf.org/html/rfc7050#section-3
175 //
176 // "A node MUST look through all of the received AAAA resource records
177 // to collect one or more Pref64::/n."
178 //
179 // TODO: Consider remedying this.
180 if (result->ai_family != AF_INET6) {
181 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId
182 << ") plat_prefix/unexpected address family: " << result->ai_family;
183 return false;
184 }
185 const IPAddress ipv6(reinterpret_cast<sockaddr_in6*>(result->ai_addr)->sin6_addr);
186 // Only /96 DNS64 prefixes are supported at this time.
187 cfg->prefix64 = IPPrefix(ipv6, 96);
188 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId << ") Detected NAT64 prefix "
189 << cfg->prefix64.toString();
190 return true;
191 }
192
isDiscoveryInProgress(const Dns64Config & cfg) const193 bool Dns64Configuration::isDiscoveryInProgress(const Dns64Config& cfg) const REQUIRES(mMutex) {
194 const auto& iter = mDns64Configs.find(cfg.netId);
195 if (iter == mDns64Configs.end()) return false;
196
197 const Dns64Config& currentCfg = iter->second;
198 return (currentCfg.discoveryId == cfg.discoveryId);
199 }
200
reportNat64PrefixStatus(unsigned netId,bool added,const IPPrefix & pfx)201 bool Dns64Configuration::reportNat64PrefixStatus(unsigned netId, bool added, const IPPrefix& pfx) {
202 if (pfx.ip().family() != AF_INET6 || pfx.ip().scope_id() != 0) {
203 LOG(WARNING) << "Abort to send NAT64 prefix notification. Unexpected NAT64 prefix ("
204 << netId << ", " << added << ", " << pfx.toString() << ").";
205 return false;
206 }
207 Nat64PrefixInfo args = {netId, added, pfx.ip().toString(), (uint8_t)pfx.length()};
208 mPrefixCallback(args);
209 return true;
210 }
211
shouldContinueDiscovery(const Dns64Config & cfg)212 bool Dns64Configuration::shouldContinueDiscovery(const Dns64Config& cfg) {
213 std::lock_guard guard(mMutex);
214 return isDiscoveryInProgress(cfg);
215 }
216
removeDns64Config(unsigned netId)217 void Dns64Configuration::removeDns64Config(unsigned netId) REQUIRES(mMutex) {
218 const auto& iter = mDns64Configs.find(netId);
219 if (iter == mDns64Configs.end()) return;
220
221 Dns64Config cfg = iter->second;
222 mDns64Configs.erase(iter);
223
224 // Only report a prefix removed event if the prefix was discovered, not if it was set.
225 if (cfg.isFromPrefixDiscovery() && !cfg.prefix64.isUninitialized()) {
226 reportNat64PrefixStatus(netId, PREFIX_REMOVED, cfg.prefix64);
227 }
228 }
229
recordDns64Config(const Dns64Config & cfg)230 void Dns64Configuration::recordDns64Config(const Dns64Config& cfg) {
231 std::lock_guard guard(mMutex);
232 if (!isDiscoveryInProgress(cfg)) return;
233
234 removeDns64Config(cfg.netId);
235 mDns64Configs.emplace(std::make_pair(cfg.netId, cfg));
236
237 reportNat64PrefixStatus(cfg.netId, PREFIX_ADDED, cfg.prefix64);
238 }
239
setPrefix64(unsigned netId,const IPPrefix & pfx)240 int Dns64Configuration::setPrefix64(unsigned netId, const IPPrefix& pfx) {
241 if (pfx.isUninitialized() || pfx.family() != AF_INET6 || pfx.length() != 96) {
242 return -EINVAL;
243 }
244
245 std::lock_guard guard(mMutex);
246
247 // This method may only be called if prefix discovery has been stopped or was never started.
248 auto iter = mDns64Configs.find(netId);
249 if (iter != mDns64Configs.end()) {
250 if (iter->second.isFromPrefixDiscovery()) {
251 return -EEXIST;
252 } else {
253 mDns64Configs.erase(iter);
254 }
255 }
256
257 Dns64Config cfg(kNoDiscoveryId, netId);
258 cfg.prefix64 = pfx;
259 mDns64Configs.emplace(std::make_pair(netId, cfg));
260
261 return 0;
262 }
263
clearPrefix64(unsigned netId)264 int Dns64Configuration::clearPrefix64(unsigned netId) {
265 std::lock_guard guard(mMutex);
266
267 const auto& iter = mDns64Configs.find(netId);
268 if (iter == mDns64Configs.end() || iter->second.isFromPrefixDiscovery()) {
269 return -ENOENT;
270 }
271
272 mDns64Configs.erase(iter);
273
274 return 0;
275 }
276
277 } // namespace net
278 } // namespace android
279