1/*
2 * Copyright (C) 2016 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
17package com.android.server.connectivity.tethering;
18
19import android.net.ConnectivityManager;
20import android.net.IpPrefix;
21import android.net.LinkAddress;
22import android.net.LinkProperties;
23import android.net.Network;
24import android.net.NetworkCapabilities;
25import android.net.NetworkState;
26import android.net.RouteInfo;
27import android.util.Log;
28
29import java.net.Inet6Address;
30import java.net.InetAddress;
31import java.util.ArrayList;
32import java.util.LinkedList;
33
34
35/**
36 * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
37 * This coordinator is responsible for evaluating the dedicated prefixes
38 * assigned to the device and deciding how to divvy them up among downstream
39 * interfaces.
40 *
41 * @hide
42 */
43public class IPv6TetheringCoordinator {
44    private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
45    private static final boolean DBG = false;
46    private static final boolean VDBG = false;
47
48    private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
49    private final LinkedList<TetherInterfaceStateMachine> mActiveDownstreams;
50    private NetworkState mUpstreamNetworkState;
51
52    public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) {
53        mNotifyList = notifyList;
54        mActiveDownstreams = new LinkedList<>();
55    }
56
57    public void addActiveDownstream(TetherInterfaceStateMachine downstream) {
58        if (mActiveDownstreams.indexOf(downstream) == -1) {
59            // Adding a new downstream appends it to the list. Adding a
60            // downstream a second time without first removing it has no effect.
61            mActiveDownstreams.offer(downstream);
62            updateIPv6TetheringInterfaces();
63        }
64    }
65
66    public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
67        stopIPv6TetheringOn(downstream);
68        if (mActiveDownstreams.remove(downstream)) {
69            updateIPv6TetheringInterfaces();
70        }
71    }
72
73    public void updateUpstreamNetworkState(NetworkState ns) {
74        if (VDBG) {
75            Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
76        }
77        if (!canTetherIPv6(ns)) {
78            stopIPv6TetheringOnAllInterfaces();
79            setUpstreamNetworkState(null);
80            return;
81        }
82
83        if (mUpstreamNetworkState != null &&
84            !ns.network.equals(mUpstreamNetworkState.network)) {
85            stopIPv6TetheringOnAllInterfaces();
86        }
87
88        setUpstreamNetworkState(ns);
89        updateIPv6TetheringInterfaces();
90    }
91
92    private void stopIPv6TetheringOnAllInterfaces() {
93        for (TetherInterfaceStateMachine sm : mNotifyList) {
94            stopIPv6TetheringOn(sm);
95        }
96    }
97
98    private void setUpstreamNetworkState(NetworkState ns) {
99        if (ns == null) {
100            mUpstreamNetworkState = null;
101        } else {
102            // Make a deep copy of the parts we need.
103            mUpstreamNetworkState = new NetworkState(
104                    null,
105                    new LinkProperties(ns.linkProperties),
106                    new NetworkCapabilities(ns.networkCapabilities),
107                    new Network(ns.network),
108                    null,
109                    null);
110        }
111
112        if (DBG) {
113            Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
114        }
115    }
116
117    private void updateIPv6TetheringInterfaces() {
118        for (TetherInterfaceStateMachine sm : mNotifyList) {
119            final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
120            sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
121            break;
122        }
123    }
124
125    private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
126        if (mUpstreamNetworkState == null) return null;
127
128        if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
129            // TODO: Figure out IPv6 support on PAN interfaces.
130            return null;
131        }
132
133        // NOTE: Here, in future, we would have policies to decide how to divvy
134        // up the available dedicated prefixes among downstream interfaces.
135        // At this time we have no such mechanism--we only support tethering
136        // IPv6 toward the oldest (first requested) active downstream.
137
138        final TetherInterfaceStateMachine currentActive = mActiveDownstreams.peek();
139        if (currentActive != null && currentActive == sm) {
140            final LinkProperties lp = getIPv6OnlyLinkProperties(
141                    mUpstreamNetworkState.linkProperties);
142            if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
143                return lp;
144            }
145        }
146
147        return null;
148    }
149
150    private static boolean canTetherIPv6(NetworkState ns) {
151        // Broadly speaking:
152        //
153        //     [1] does the upstream have an IPv6 default route?
154        //
155        // and
156        //
157        //     [2] does the upstream have one or more global IPv6 /64s
158        //         dedicated to this device?
159        //
160        // In lieu of Prefix Delegation and other evaluation of whether a
161        // prefix may or may not be dedicated to this device, for now just
162        // check whether the upstream is TRANSPORT_CELLULAR. This works
163        // because "[t]he 3GPP network allocates each default bearer a unique
164        // /64 prefix", per RFC 6459, Section 5.2.
165
166        final boolean canTether =
167                (ns != null) && (ns.network != null) &&
168                (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
169                // At least one upstream DNS server:
170                ns.linkProperties.isProvisioned() &&
171                // Minimal amount of IPv6 provisioning:
172                ns.linkProperties.hasIPv6DefaultRoute() &&
173                ns.linkProperties.hasGlobalIPv6Address() &&
174                // Temporary approximation of "dedicated prefix":
175                ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
176
177        // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
178        // tethering with 464xlat involved). TODO: Rectify this shortcoming,
179        // likely by calling NetworkManagementService#startInterfaceForwarding()
180        // for all upstream interfaces.
181        RouteInfo v4default = null;
182        RouteInfo v6default = null;
183        if (canTether) {
184            for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
185                if (r.isIPv4Default()) {
186                    v4default = r;
187                } else if (r.isIPv6Default()) {
188                    v6default = r;
189                }
190
191                if (v4default != null && v6default != null) {
192                    break;
193                }
194            }
195        }
196
197        final boolean supportedConfiguration =
198                (v4default != null) && (v6default != null) &&
199                (v4default.getInterface() != null) &&
200                v4default.getInterface().equals(v6default.getInterface());
201
202        final boolean outcome = canTether && supportedConfiguration;
203
204        if (VDBG) {
205            if (ns == null) {
206                Log.d(TAG, "No available upstream.");
207            } else {
208                Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s",
209                        (outcome ? "available" : "not available"), toDebugString(ns)));
210            }
211        }
212
213        return outcome;
214    }
215
216    private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
217        final LinkProperties v6only = new LinkProperties();
218        if (lp == null) {
219            return v6only;
220        }
221
222        // NOTE: At this time we don't copy over any information about any
223        // stacked links. No current stacked link configuration has IPv6.
224
225        v6only.setInterfaceName(lp.getInterfaceName());
226
227        v6only.setMtu(lp.getMtu());
228
229        for (LinkAddress linkAddr : lp.getLinkAddresses()) {
230            if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
231                v6only.addLinkAddress(linkAddr);
232            }
233        }
234
235        for (RouteInfo routeInfo : lp.getRoutes()) {
236            final IpPrefix destination = routeInfo.getDestination();
237            if ((destination.getAddress() instanceof Inet6Address) &&
238                (destination.getPrefixLength() <= 64)) {
239                v6only.addRoute(routeInfo);
240            }
241        }
242
243        for (InetAddress dnsServer : lp.getDnsServers()) {
244            if (isIPv6GlobalAddress(dnsServer)) {
245                // For now we include ULAs.
246                v6only.addDnsServer(dnsServer);
247            }
248        }
249
250        v6only.setDomains(lp.getDomains());
251
252        return v6only;
253    }
254
255    // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
256    // announce our own IPv6 address as DNS server.
257    private static boolean isIPv6GlobalAddress(InetAddress ip) {
258        return (ip instanceof Inet6Address) &&
259               !ip.isAnyLocalAddress() &&
260               !ip.isLoopbackAddress() &&
261               !ip.isLinkLocalAddress() &&
262               !ip.isSiteLocalAddress() &&
263               !ip.isMulticastAddress();
264    }
265
266    private static String toDebugString(NetworkState ns) {
267        if (ns == null) {
268            return "NetworkState{null}";
269        }
270        return String.format("NetworkState{%s, %s, %s}",
271                ns.network,
272                ns.networkCapabilities,
273                ns.linkProperties);
274    }
275
276    private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
277        sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
278    }
279}
280