blob: a1fe87178aeb2f738efb8ae876d5006a7a8a7ceb [file] [log] [blame]
Luke Huang7b26b202019-03-28 14:09:24 +08001/*
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
Ken Chen5471dca2019-04-15 15:25:35 +080017#define LOG_TAG "resolv"
Luke Huang7b26b202019-03-28 14:09:24 +080018
19#include "Dns64Configuration.h"
20
chenbruceaff85842019-05-31 15:46:42 +080021#include <android-base/logging.h>
Luke Huang7b26b202019-03-28 14:09:24 +080022#include <netdb.h>
Luke Huang9d2d25b2019-06-14 00:34:05 +080023#include <netdutils/BackoffSequence.h>
24#include <netdutils/DumpWriter.h>
25#include <netdutils/InternetAddresses.h>
Mike Yu04f1d482019-08-08 11:09:32 +080026#include <netdutils/ThreadUtil.h>
Luke Huang7b26b202019-03-28 14:09:24 +080027#include <thread>
28#include <utility>
29
30#include <arpa/inet.h>
31
32#include "DnsResolver.h"
Bernie Innocentie71a28a2019-05-29 00:42:35 +090033#include "getaddrinfo.h"
Luke Huang7b26b202019-03-28 14:09:24 +080034#include "netd_resolv/resolv.h"
lifr94981782019-05-17 21:15:19 +080035#include "stats.pb.h"
Luke Huang7b26b202019-03-28 14:09:24 +080036
37namespace android {
38
39using netdutils::DumpWriter;
40using netdutils::IPAddress;
41using netdutils::IPPrefix;
Luke Huang9d2d25b2019-06-14 00:34:05 +080042using netdutils::ScopedAddrinfo;
Mike Yucb902c92020-05-20 19:26:56 +080043using netdutils::setThreadName;
Luke Huang7b26b202019-03-28 14:09:24 +080044
45namespace net {
46
47const char Dns64Configuration::kIPv4OnlyHost[] = "ipv4only.arpa.";
48const char Dns64Configuration::kIPv4Literal1[] = "192.0.0.170";
49const char Dns64Configuration::kIPv4Literal2[] = "192.0.0.171";
50
51void 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 // Note that capturing |cfg| in this lambda creates a copy.
Mike Yu04f1d482019-08-08 11:09:32 +080065 std::thread discovery_thread([this, cfg, netId] {
chenbruce9b72daa2021-08-20 00:00:49 +080066 setThreadName(fmt::format("Nat64Pfx_{}", netId));
Mike Yu04f1d482019-08-08 11:09:32 +080067
Luke Huang7b26b202019-03-28 14:09:24 +080068 // Make a mutable copy rather than mark the whole lambda mutable.
69 // No particular reason.
70 Dns64Config evalCfg(cfg);
71
72 auto backoff = netdutils::BackoffSequence<>::Builder()
73 .withInitialRetransmissionTime(std::chrono::seconds(1))
74 .withMaximumRetransmissionTime(std::chrono::seconds(3600))
75 .build();
76
77 while (true) {
78 if (!this->shouldContinueDiscovery(evalCfg)) break;
79
80 android_net_context netcontext{};
81 mGetNetworkContextCallback(evalCfg.netId, 0, &netcontext);
82
83 // Prefix discovery must bypass private DNS because in strict mode
84 // the server generally won't know the NAT64 prefix.
85 netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS;
86 if (doRfc7050PrefixDiscovery(netcontext, &evalCfg)) {
87 this->recordDns64Config(evalCfg);
88 break;
89 }
90
91 if (!this->shouldContinueDiscovery(evalCfg)) break;
92
93 if (!backoff.hasNextTimeout()) break;
94 {
95 std::unique_lock<std::mutex> cvGuard(mMutex);
96 // TODO: Consider some chrono math, combined with wait_until()
97 // perhaps, to prevent early re-resolves from the removal of
98 // other netids with IPv6-only nameservers.
99 mCv.wait_for(cvGuard, backoff.getNextTimeout());
100 }
101 }
102 });
103 discovery_thread.detach();
104}
105
106void Dns64Configuration::stopPrefixDiscovery(unsigned netId) {
107 std::lock_guard guard(mMutex);
108 removeDns64Config(netId);
109 mCv.notify_all();
110}
111
112IPPrefix Dns64Configuration::getPrefix64Locked(unsigned netId) const REQUIRES(mMutex) {
113 const auto& iter = mDns64Configs.find(netId);
114 if (iter != mDns64Configs.end()) return iter->second.prefix64;
115
116 return IPPrefix{};
117}
118
119IPPrefix Dns64Configuration::getPrefix64(unsigned netId) const {
120 std::lock_guard guard(mMutex);
121 return getPrefix64Locked(netId);
122}
123
124void Dns64Configuration::dump(DumpWriter& dw, unsigned netId) {
125 static const char kLabel[] = "DNS64 config";
126
127 std::lock_guard guard(mMutex);
128
129 const auto& iter = mDns64Configs.find(netId);
130 if (iter == mDns64Configs.end()) {
131 dw.println("%s: none", kLabel);
132 return;
133 }
134
135 const Dns64Config& cfg = iter->second;
136 if (cfg.prefix64.length() == 0) {
137 dw.println("%s: no prefix yet discovered", kLabel);
138 } else {
Lorenzo Colitti511b6d42020-05-21 22:57:31 +0900139 dw.println("%s: %s prefix %s", kLabel, cfg.isFromPrefixDiscovery() ? "discovered" : "set",
140 cfg.prefix64.toString().c_str());
Luke Huang7b26b202019-03-28 14:09:24 +0800141 }
142}
143
144// NOTE: The full RFC 7050 DNS64 discovery process is not implemented here.
145// Instead, and more simplistic version of the same thing is done, and it
146// currently assumes the DNS64 prefix is a /96.
147bool Dns64Configuration::doRfc7050PrefixDiscovery(const android_net_context& netcontext,
148 Dns64Config* cfg) {
chenbruceaff85842019-05-31 15:46:42 +0800149 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId
150 << ") Detecting NAT64 prefix from DNS...";
Luke Huang7b26b202019-03-28 14:09:24 +0800151
152 const struct addrinfo hints = {
153 .ai_family = AF_INET6,
154 };
155
156 // TODO: Refactor so that netd can get all the regular getaddrinfo handling
157 // that regular apps get. We bypass the UNIX socket connection back to
158 // ourselves, which means we also bypass all the special netcontext flag
159 // handling and the resolver event logging.
160 struct addrinfo* res = nullptr;
lifr94981782019-05-17 21:15:19 +0800161 NetworkDnsEventReported event;
162 const int status =
163 resolv_getaddrinfo(kIPv4OnlyHost, nullptr, &hints, &netcontext, &res, &event);
Luke Huang7b26b202019-03-28 14:09:24 +0800164 ScopedAddrinfo result(res);
165 if (status != 0) {
chenbruceaff85842019-05-31 15:46:42 +0800166 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId << ") plat_prefix/dns("
167 << kIPv4OnlyHost << ") status = " << status << "/" << gai_strerror(status);
Luke Huang7b26b202019-03-28 14:09:24 +0800168 return false;
169 }
170
171 // Use only the first result. If other records are present, possibly
172 // with differing DNS64 prefixes they are ignored. Note that this is a
173 // violation of https://tools.ietf.org/html/rfc7050#section-3
174 //
175 // "A node MUST look through all of the received AAAA resource records
176 // to collect one or more Pref64::/n."
177 //
178 // TODO: Consider remedying this.
179 if (result->ai_family != AF_INET6) {
chenbruceaff85842019-05-31 15:46:42 +0800180 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId
181 << ") plat_prefix/unexpected address family: " << result->ai_family;
Luke Huang7b26b202019-03-28 14:09:24 +0800182 return false;
183 }
184 const IPAddress ipv6(reinterpret_cast<sockaddr_in6*>(result->ai_addr)->sin6_addr);
185 // Only /96 DNS64 prefixes are supported at this time.
186 cfg->prefix64 = IPPrefix(ipv6, 96);
chenbruceaff85842019-05-31 15:46:42 +0800187 LOG(WARNING) << "(" << cfg->netId << ", " << cfg->discoveryId << ") Detected NAT64 prefix "
188 << cfg->prefix64.toString();
Luke Huang7b26b202019-03-28 14:09:24 +0800189 return true;
190}
191
192bool Dns64Configuration::isDiscoveryInProgress(const Dns64Config& cfg) const REQUIRES(mMutex) {
193 const auto& iter = mDns64Configs.find(cfg.netId);
194 if (iter == mDns64Configs.end()) return false;
195
196 const Dns64Config& currentCfg = iter->second;
197 return (currentCfg.discoveryId == cfg.discoveryId);
198}
199
200bool Dns64Configuration::reportNat64PrefixStatus(unsigned netId, bool added, const IPPrefix& pfx) {
201 if (pfx.ip().family() != AF_INET6 || pfx.ip().scope_id() != 0) {
chenbruceaff85842019-05-31 15:46:42 +0800202 LOG(WARNING) << "Abort to send NAT64 prefix notification. Unexpected NAT64 prefix ("
203 << netId << ", " << added << ", " << pfx.toString() << ").";
Luke Huang7b26b202019-03-28 14:09:24 +0800204 return false;
205 }
206 Nat64PrefixInfo args = {netId, added, pfx.ip().toString(), (uint8_t)pfx.length()};
207 mPrefixCallback(args);
208 return true;
209}
210
211bool Dns64Configuration::shouldContinueDiscovery(const Dns64Config& cfg) {
212 std::lock_guard guard(mMutex);
213 return isDiscoveryInProgress(cfg);
214}
215
216void Dns64Configuration::removeDns64Config(unsigned netId) REQUIRES(mMutex) {
Lorenzo Colittie51091d2020-05-20 02:48:44 +0900217 const auto& iter = mDns64Configs.find(netId);
218 if (iter == mDns64Configs.end()) return;
219
220 Dns64Config cfg = iter->second;
221 mDns64Configs.erase(iter);
222
223 // Only report a prefix removed event if the prefix was discovered, not if it was set.
224 if (cfg.isFromPrefixDiscovery() && !cfg.prefix64.isUninitialized()) {
225 reportNat64PrefixStatus(netId, PREFIX_REMOVED, cfg.prefix64);
Luke Huang7b26b202019-03-28 14:09:24 +0800226 }
227}
228
229void Dns64Configuration::recordDns64Config(const Dns64Config& cfg) {
230 std::lock_guard guard(mMutex);
231 if (!isDiscoveryInProgress(cfg)) return;
232
233 removeDns64Config(cfg.netId);
234 mDns64Configs.emplace(std::make_pair(cfg.netId, cfg));
235
236 reportNat64PrefixStatus(cfg.netId, PREFIX_ADDED, cfg.prefix64);
Lorenzo Colittie51091d2020-05-20 02:48:44 +0900237}
Luke Huang7b26b202019-03-28 14:09:24 +0800238
Lorenzo Colitti511b6d42020-05-21 22:57:31 +0900239int Dns64Configuration::setPrefix64(unsigned netId, const IPPrefix& pfx) {
240 if (pfx.isUninitialized() || pfx.family() != AF_INET6 || pfx.length() != 96) {
Lorenzo Colittie51091d2020-05-20 02:48:44 +0900241 return -EINVAL;
242 }
243
244 std::lock_guard guard(mMutex);
245
246 // This method may only be called if prefix discovery has been stopped or was never started.
247 auto iter = mDns64Configs.find(netId);
248 if (iter != mDns64Configs.end()) {
249 if (iter->second.isFromPrefixDiscovery()) {
250 return -EEXIST;
251 } else {
252 mDns64Configs.erase(iter);
253 }
254 }
255
256 Dns64Config cfg(kNoDiscoveryId, netId);
Lorenzo Colitti511b6d42020-05-21 22:57:31 +0900257 cfg.prefix64 = pfx;
Lorenzo Colittie51091d2020-05-20 02:48:44 +0900258 mDns64Configs.emplace(std::make_pair(netId, cfg));
259
260 return 0;
261}
262
263int Dns64Configuration::clearPrefix64(unsigned netId) {
264 std::lock_guard guard(mMutex);
265
266 const auto& iter = mDns64Configs.find(netId);
267 if (iter == mDns64Configs.end() || iter->second.isFromPrefixDiscovery()) {
268 return -ENOENT;
269 }
270
271 mDns64Configs.erase(iter);
272
273 return 0;
Luke Huang7b26b202019-03-28 14:09:24 +0800274}
275
276} // namespace net
277} // namespace android