1 /*
2  * Copyright (C) 2012 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 package com.android.server.connectivity;
18 
19 import android.annotation.NonNull;
20 import android.net.ConnectivityManager;
21 import android.net.IDnsResolver;
22 import android.net.INetd;
23 import android.net.InetAddresses;
24 import android.net.InterfaceConfiguration;
25 import android.net.IpPrefix;
26 import android.net.LinkAddress;
27 import android.net.LinkProperties;
28 import android.net.NetworkInfo;
29 import android.net.RouteInfo;
30 import android.os.INetworkManagementService;
31 import android.os.RemoteException;
32 import android.os.ServiceSpecificException;
33 import android.util.Slog;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.internal.util.ArrayUtils;
37 import com.android.server.net.BaseNetworkObserver;
38 
39 import java.net.Inet4Address;
40 import java.net.Inet6Address;
41 import java.util.Objects;
42 
43 /**
44  * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
45  * from a consistent and unique thread context. It is the responsibility of ConnectivityService to
46  * call into this class from its own Handler thread.
47  *
48  * @hide
49  */
50 public class Nat464Xlat extends BaseNetworkObserver {
51     private static final String TAG = Nat464Xlat.class.getSimpleName();
52 
53     // This must match the interface prefix in clatd.c.
54     private static final String CLAT_PREFIX = "v4-";
55 
56     // The network types on which we will start clatd,
57     // allowing clat only on networks for which we can support IPv6-only.
58     private static final int[] NETWORK_TYPES = {
59         ConnectivityManager.TYPE_MOBILE,
60         ConnectivityManager.TYPE_WIFI,
61         ConnectivityManager.TYPE_ETHERNET,
62     };
63 
64     // The network states in which running clatd is supported.
65     private static final NetworkInfo.State[] NETWORK_STATES = {
66         NetworkInfo.State.CONNECTED,
67         NetworkInfo.State.SUSPENDED,
68     };
69 
70     private final IDnsResolver mDnsResolver;
71     private final INetd mNetd;
72     private final INetworkManagementService mNMService;
73 
74     // The network we're running on, and its type.
75     private final NetworkAgentInfo mNetwork;
76 
77     private enum State {
78         IDLE,         // start() not called. Base iface and stacked iface names are null.
79         DISCOVERING,  // same as IDLE, except prefix discovery in progress.
80         STARTING,     // start() called. Base iface and stacked iface names are known.
81         RUNNING,      // start() called, and the stacked iface is known to be up.
82     }
83 
84     /**
85      * NAT64 prefix currently in use. Only valid in STARTING or RUNNING states.
86      * Used, among other things, to avoid updates when switching from a prefix learned from one
87      * source (e.g., RA) to the same prefix learned from another source (e.g., RA).
88      */
89     private IpPrefix mNat64PrefixInUse;
90     /** NAT64 prefix (if any) discovered from DNS via RFC 7050. */
91     private IpPrefix mNat64PrefixFromDns;
92     /** NAT64 prefix (if any) learned from the network via RA. */
93     private IpPrefix mNat64PrefixFromRa;
94     private String mBaseIface;
95     private String mIface;
96     private Inet6Address mIPv6Address;
97     private State mState = State.IDLE;
98 
99     private boolean mPrefixDiscoveryRunning;
100 
Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver, INetworkManagementService nmService)101     public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
102             INetworkManagementService nmService) {
103         mDnsResolver = dnsResolver;
104         mNetd = netd;
105         mNMService = nmService;
106         mNetwork = nai;
107     }
108 
109     /**
110      * Whether to attempt 464xlat on this network. This is true for an IPv6-only network that is
111      * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to
112      * enable NAT64 prefix discovery.
113      *
114      * @param nai the NetworkAgentInfo corresponding to the network.
115      * @return true if the network requires clat, false otherwise.
116      */
117     @VisibleForTesting
requiresClat(NetworkAgentInfo nai)118     protected static boolean requiresClat(NetworkAgentInfo nai) {
119         // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
120         final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
121         final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState());
122 
123         // Only run clat on networks that have a global IPv6 address and don't have a native IPv4
124         // address.
125         LinkProperties lp = nai.linkProperties;
126         final boolean isIpv6OnlyNetwork = (lp != null) && lp.hasGlobalIpv6Address()
127                 && !lp.hasIpv4Address();
128 
129         // If the network tells us it doesn't use clat, respect that.
130         final boolean skip464xlat = (nai.netAgentConfig() != null)
131                 && nai.netAgentConfig().skip464xlat;
132 
133         return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
134     }
135 
136     /**
137      * Whether the clat demon should be started on this network now. This is true if requiresClat is
138      * true and a NAT64 prefix has been discovered.
139      *
140      * @param nai the NetworkAgentInfo corresponding to the network.
141      * @return true if the network should start clat, false otherwise.
142      */
143     @VisibleForTesting
shouldStartClat(NetworkAgentInfo nai)144     protected static boolean shouldStartClat(NetworkAgentInfo nai) {
145         LinkProperties lp = nai.linkProperties;
146         return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
147     }
148 
149     /**
150      * @return true if clatd has been started and has not yet stopped.
151      * A true result corresponds to internal states STARTING and RUNNING.
152      */
isStarted()153     public boolean isStarted() {
154         return (mState == State.STARTING || mState == State.RUNNING);
155     }
156 
157     /**
158      * @return true if clatd has been started but the stacked interface is not yet up.
159      */
isStarting()160     public boolean isStarting() {
161         return mState == State.STARTING;
162     }
163 
164     /**
165      * @return true if clatd has been started and the stacked interface is up.
166      */
isRunning()167     public boolean isRunning() {
168         return mState == State.RUNNING;
169     }
170 
171     /**
172      * Start clatd, register this Nat464Xlat as a network observer for the stacked interface,
173      * and set internal state.
174      */
enterStartingState(String baseIface)175     private void enterStartingState(String baseIface) {
176         try {
177             mNMService.registerObserver(this);
178         } catch (RemoteException e) {
179             Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
180             return;
181         }
182 
183         mNat64PrefixInUse = selectNat64Prefix();
184         String addrStr = null;
185         try {
186             addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
187         } catch (RemoteException | ServiceSpecificException e) {
188             Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
189         }
190         mIface = CLAT_PREFIX + baseIface;
191         mBaseIface = baseIface;
192         mState = State.STARTING;
193         try {
194             mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr);
195         } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
196             Slog.e(TAG, "Invalid IPv6 address " + addrStr);
197         }
198         if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
199             stopPrefixDiscovery();
200         }
201         if (!mPrefixDiscoveryRunning) {
202             setPrefix64(mNat64PrefixInUse);
203         }
204     }
205 
206     /**
207      * Enter running state just after getting confirmation that the stacked interface is up, and
208      * turn ND offload off if on WiFi.
209      */
enterRunningState()210     private void enterRunningState() {
211         mState = State.RUNNING;
212     }
213 
214     /**
215      * Unregister as a base observer for the stacked interface, and clear internal state.
216      */
leaveStartedState()217     private void leaveStartedState() {
218         try {
219             mNMService.unregisterObserver(this);
220         } catch (RemoteException | IllegalStateException e) {
221             Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
222         }
223         mNat64PrefixInUse = null;
224         mIface = null;
225         mBaseIface = null;
226 
227         if (!mPrefixDiscoveryRunning) {
228             setPrefix64(null);
229         }
230 
231         if (isPrefixDiscoveryNeeded()) {
232             if (!mPrefixDiscoveryRunning) {
233                 startPrefixDiscovery();
234             }
235             mState = State.DISCOVERING;
236         } else {
237             stopPrefixDiscovery();
238             mState = State.IDLE;
239         }
240     }
241 
242     @VisibleForTesting
start()243     protected void start() {
244         if (isStarted()) {
245             Slog.e(TAG, "startClat: already started");
246             return;
247         }
248 
249         if (mNetwork.linkProperties == null) {
250             Slog.e(TAG, "startClat: Can't start clat with null LinkProperties");
251             return;
252         }
253 
254         String baseIface = mNetwork.linkProperties.getInterfaceName();
255         if (baseIface == null) {
256             Slog.e(TAG, "startClat: Can't start clat on null interface");
257             return;
258         }
259         // TODO: should we only do this if mNetd.clatdStart() succeeds?
260         Slog.i(TAG, "Starting clatd on " + baseIface);
261         enterStartingState(baseIface);
262     }
263 
264     @VisibleForTesting
stop()265     protected void stop() {
266         if (!isStarted()) {
267             Slog.e(TAG, "stopClat: already stopped");
268             return;
269         }
270 
271         Slog.i(TAG, "Stopping clatd on " + mBaseIface);
272         try {
273             mNetd.clatdStop(mBaseIface);
274         } catch (RemoteException | ServiceSpecificException e) {
275             Slog.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
276         }
277 
278         String iface = mIface;
279         boolean wasRunning = isRunning();
280 
281         // Change state before updating LinkProperties. handleUpdateLinkProperties ends up calling
282         // fixupLinkProperties, and if at that time the state is still RUNNING, fixupLinkProperties
283         // would wrongly inform ConnectivityService that there is still a stacked interface.
284         leaveStartedState();
285 
286         if (wasRunning) {
287             LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
288             lp.removeStackedLink(iface);
289             mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
290         }
291     }
292 
startPrefixDiscovery()293     private void startPrefixDiscovery() {
294         try {
295             mDnsResolver.startPrefix64Discovery(getNetId());
296         } catch (RemoteException | ServiceSpecificException e) {
297             Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
298         }
299         mPrefixDiscoveryRunning = true;
300     }
301 
stopPrefixDiscovery()302     private void stopPrefixDiscovery() {
303         try {
304             mDnsResolver.stopPrefix64Discovery(getNetId());
305         } catch (RemoteException | ServiceSpecificException e) {
306             Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
307         }
308         mPrefixDiscoveryRunning = false;
309     }
310 
isPrefixDiscoveryNeeded()311     private boolean isPrefixDiscoveryNeeded() {
312         // If there is no NAT64 prefix in the RA, prefix discovery is always needed. It cannot be
313         // stopped after it succeeds, because stopping it will cause netd to report that the prefix
314         // has been removed, and that will cause us to stop clatd.
315         return requiresClat(mNetwork) && mNat64PrefixFromRa == null;
316     }
317 
setPrefix64(IpPrefix prefix)318     private void setPrefix64(IpPrefix prefix) {
319         final String prefixString = (prefix != null) ? prefix.toString() : "";
320         try {
321             mDnsResolver.setPrefix64(getNetId(), prefixString);
322         } catch (RemoteException | ServiceSpecificException e) {
323             Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
324                     + prefix + ": " + e);
325         }
326     }
327 
maybeHandleNat64PrefixChange()328     private void maybeHandleNat64PrefixChange() {
329         final IpPrefix newPrefix = selectNat64Prefix();
330         if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
331             Slog.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
332                     + newPrefix);
333             stop();
334             // It's safe to call update here, even though this method is called from update, because
335             // stop() is guaranteed to have moved out of STARTING and RUNNING, which are the only
336             // states in which this method can be called.
337             update();
338         }
339     }
340 
341     /**
342      * Starts/stops NAT64 prefix discovery and clatd as necessary.
343      */
update()344     public void update() {
345         // TODO: turn this class into a proper StateMachine. http://b/126113090
346         switch (mState) {
347             case IDLE:
348                 if (isPrefixDiscoveryNeeded()) {
349                     startPrefixDiscovery();  // Enters DISCOVERING state.
350                     mState = State.DISCOVERING;
351                 } else if (requiresClat(mNetwork)) {
352                     start();  // Enters STARTING state.
353                 }
354                 break;
355 
356             case DISCOVERING:
357                 if (shouldStartClat(mNetwork)) {
358                     // NAT64 prefix detected. Start clatd.
359                     start();  // Enters STARTING state.
360                     return;
361                 }
362                 if (!requiresClat(mNetwork)) {
363                     // IPv4 address added. Go back to IDLE state.
364                     stopPrefixDiscovery();
365                     mState = State.IDLE;
366                     return;
367                 }
368                 break;
369 
370             case STARTING:
371             case RUNNING:
372                 // NAT64 prefix removed, or IPv4 address added.
373                 // Stop clatd and go back into DISCOVERING or idle.
374                 if (!shouldStartClat(mNetwork)) {
375                     stop();
376                     break;
377                 }
378                 // Only necessary while clat is actually started.
379                 maybeHandleNat64PrefixChange();
380                 break;
381         }
382     }
383 
384     /**
385      * Picks a NAT64 prefix to use. Always prefers the prefix from the RA if one is received from
386      * both RA and DNS, because the prefix in the RA has better security and updatability, and will
387      * almost always be received first anyway.
388      *
389      * Any network that supports legacy hosts will support discovering the DNS64 prefix via DNS as
390      * well. If the prefix from the RA is withdrawn, fall back to that for reliability purposes.
391      */
selectNat64Prefix()392     private IpPrefix selectNat64Prefix() {
393         return mNat64PrefixFromRa != null ? mNat64PrefixFromRa : mNat64PrefixFromDns;
394     }
395 
setNat64PrefixFromRa(IpPrefix prefix)396     public void setNat64PrefixFromRa(IpPrefix prefix) {
397         mNat64PrefixFromRa = prefix;
398     }
399 
setNat64PrefixFromDns(IpPrefix prefix)400     public void setNat64PrefixFromDns(IpPrefix prefix) {
401         mNat64PrefixFromDns = prefix;
402     }
403 
404     /**
405      * Copies the stacked clat link in oldLp, if any, to the passed LinkProperties.
406      * This is necessary because the LinkProperties in mNetwork come from the transport layer, which
407      * has no idea that 464xlat is running on top of it.
408      */
fixupLinkProperties(@onNull LinkProperties oldLp, @NonNull LinkProperties lp)409     public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
410         // This must be done even if clatd is not running, because otherwise shouldStartClat would
411         // never return true.
412         lp.setNat64Prefix(selectNat64Prefix());
413 
414         if (!isRunning()) {
415             return;
416         }
417         if (lp.getAllInterfaceNames().contains(mIface)) {
418             return;
419         }
420 
421         Slog.d(TAG, "clatd running, updating NAI for " + mIface);
422         for (LinkProperties stacked: oldLp.getStackedLinks()) {
423             if (Objects.equals(mIface, stacked.getInterfaceName())) {
424                 lp.addStackedLink(stacked);
425                 return;
426             }
427         }
428     }
429 
makeLinkProperties(LinkAddress clatAddress)430     private LinkProperties makeLinkProperties(LinkAddress clatAddress) {
431         LinkProperties stacked = new LinkProperties();
432         stacked.setInterfaceName(mIface);
433 
434         // Although the clat interface is a point-to-point tunnel, we don't
435         // point the route directly at the interface because some apps don't
436         // understand routes without gateways (see, e.g., http://b/9597256
437         // http://b/9597516). Instead, set the next hop of the route to the
438         // clat IPv4 address itself (for those apps, it doesn't matter what
439         // the IP of the gateway is, only that there is one).
440         RouteInfo ipv4Default = new RouteInfo(
441                 new LinkAddress(Inet4Address.ANY, 0),
442                 clatAddress.getAddress(), mIface);
443         stacked.addRoute(ipv4Default);
444         stacked.addLinkAddress(clatAddress);
445         return stacked;
446     }
447 
getLinkAddress(String iface)448     private LinkAddress getLinkAddress(String iface) {
449         try {
450             InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
451             return config.getLinkAddress();
452         } catch (RemoteException | IllegalStateException e) {
453             Slog.e(TAG, "Error getting link properties: " + e);
454             return null;
455         }
456     }
457 
458     /**
459      * Adds stacked link on base link and transitions to RUNNING state.
460      */
handleInterfaceLinkStateChanged(String iface, boolean up)461     private void handleInterfaceLinkStateChanged(String iface, boolean up) {
462         // TODO: if we call start(), then stop(), then start() again, and the
463         // interfaceLinkStateChanged notification for the first start is delayed past the first
464         // stop, then the code becomes out of sync with system state and will behave incorrectly.
465         //
466         // This is not trivial to fix because:
467         // 1. It is not guaranteed that start() will eventually result in the interface coming up,
468         //    because there could be an error starting clat (e.g., if the interface goes down before
469         //    the packet socket can be bound).
470         // 2. If start is called multiple times, there is nothing in the interfaceLinkStateChanged
471         //    notification that says which start() call the interface was created by.
472         //
473         // Once this code is converted to StateMachine, it will be possible to use deferMessage to
474         // ensure it stays in STARTING state until the interfaceLinkStateChanged notification fires,
475         // and possibly use a timeout (or provide some guarantees at the lower layer) to address #1.
476         if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
477             return;
478         }
479 
480         LinkAddress clatAddress = getLinkAddress(iface);
481         if (clatAddress == null) {
482             Slog.e(TAG, "clatAddress was null for stacked iface " + iface);
483             return;
484         }
485 
486         Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
487                 mIface, mIface, mBaseIface));
488         enterRunningState();
489         LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
490         lp.addStackedLink(makeLinkProperties(clatAddress));
491         mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
492     }
493 
494     /**
495      * Removes stacked link on base link and transitions to IDLE state.
496      */
handleInterfaceRemoved(String iface)497     private void handleInterfaceRemoved(String iface) {
498         if (!Objects.equals(mIface, iface)) {
499             return;
500         }
501         if (!isRunning()) {
502             return;
503         }
504 
505         Slog.i(TAG, "interface " + iface + " removed");
506         // If we're running, and the interface was removed, then we didn't call stop(), and it's
507         // likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling
508         // stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update
509         // will cause ConnectivityService to call start() again.
510         stop();
511     }
512 
513     @Override
interfaceLinkStateChanged(String iface, boolean up)514     public void interfaceLinkStateChanged(String iface, boolean up) {
515         mNetwork.handler().post(() -> { handleInterfaceLinkStateChanged(iface, up); });
516     }
517 
518     @Override
interfaceRemoved(String iface)519     public void interfaceRemoved(String iface) {
520         mNetwork.handler().post(() -> handleInterfaceRemoved(iface));
521     }
522 
523     @Override
toString()524     public String toString() {
525         return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
526     }
527 
528     @VisibleForTesting
getNetId()529     protected int getNetId() {
530         return mNetwork.network.netId;
531     }
532 }
533