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