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