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