• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.connectivity.tethering;
18 
19 import android.net.ConnectivityManager;
20 import android.net.IpPrefix;
21 import android.net.LinkAddress;
22 import android.net.LinkProperties;
23 import android.net.Network;
24 import android.net.NetworkCapabilities;
25 import android.net.NetworkState;
26 import android.net.RouteInfo;
27 import android.net.util.NetworkConstants;
28 import android.net.util.SharedLog;
29 import android.util.Log;
30 
31 import java.net.Inet6Address;
32 import java.net.InetAddress;
33 import java.net.UnknownHostException;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.LinkedList;
38 import java.util.Random;
39 
40 
41 /**
42  * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
43  * This coordinator is responsible for evaluating the dedicated prefixes
44  * assigned to the device and deciding how to divvy them up among downstream
45  * interfaces.
46  *
47  * @hide
48  */
49 public class IPv6TetheringCoordinator {
50     private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
51     private static final boolean DBG = false;
52     private static final boolean VDBG = false;
53 
54     private static class Downstream {
55         public final TetherInterfaceStateMachine tism;
56         public final int mode;  // IControlsTethering.STATE_*
57         // Used to append to a ULA /48, constructing a ULA /64 for local use.
58         public final short subnetId;
59 
Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId)60         Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId) {
61             this.tism = tism;
62             this.mode = mode;
63             this.subnetId = subnetId;
64         }
65     }
66 
67     private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
68     private final SharedLog mLog;
69     // NOTE: mActiveDownstreams is a list and not a hash data structure because
70     // we keep active downstreams in arrival order.  This is done so /64s can
71     // be parceled out on a "first come, first served" basis and a /64 used by
72     // a downstream that is no longer active can be redistributed to any next
73     // waiting active downstream (again, in arrival order).
74     private final LinkedList<Downstream> mActiveDownstreams;
75     private final byte[] mUniqueLocalPrefix;
76     private short mNextSubnetId;
77     private NetworkState mUpstreamNetworkState;
78 
IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log)79     public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
80                                     SharedLog log) {
81         mNotifyList = notifyList;
82         mLog = log.forSubComponent(TAG);
83         mActiveDownstreams = new LinkedList<>();
84         mUniqueLocalPrefix = generateUniqueLocalPrefix();
85         mNextSubnetId = 0;
86     }
87 
addActiveDownstream(TetherInterfaceStateMachine downstream, int mode)88     public void addActiveDownstream(TetherInterfaceStateMachine downstream, int mode) {
89         if (findDownstream(downstream) == null) {
90             // Adding a new downstream appends it to the list. Adding a
91             // downstream a second time without first removing it has no effect.
92             // We never change the mode of a downstream except by first removing
93             // it and then re-adding it (with its new mode specified);
94             if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) {
95                 // Make sure subnet IDs are always positive. They are appended
96                 // to a ULA /48 to make a ULA /64 for local use.
97                 mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1);
98             }
99             updateIPv6TetheringInterfaces();
100         }
101     }
102 
removeActiveDownstream(TetherInterfaceStateMachine downstream)103     public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
104         stopIPv6TetheringOn(downstream);
105         if (mActiveDownstreams.remove(findDownstream(downstream))) {
106             updateIPv6TetheringInterfaces();
107         }
108 
109         // When tethering is stopping we can reset the subnet counter.
110         if (mNotifyList.isEmpty()) {
111             if (!mActiveDownstreams.isEmpty()) {
112                 Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty.");
113             }
114             mNextSubnetId = 0;
115         }
116     }
117 
updateUpstreamNetworkState(NetworkState ns)118     public void updateUpstreamNetworkState(NetworkState ns) {
119         if (VDBG) {
120             Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
121         }
122         if (!canTetherIPv6(ns, mLog)) {
123             stopIPv6TetheringOnAllInterfaces();
124             setUpstreamNetworkState(null);
125             return;
126         }
127 
128         if (mUpstreamNetworkState != null &&
129             !ns.network.equals(mUpstreamNetworkState.network)) {
130             stopIPv6TetheringOnAllInterfaces();
131         }
132 
133         setUpstreamNetworkState(ns);
134         updateIPv6TetheringInterfaces();
135     }
136 
stopIPv6TetheringOnAllInterfaces()137     private void stopIPv6TetheringOnAllInterfaces() {
138         for (TetherInterfaceStateMachine sm : mNotifyList) {
139             stopIPv6TetheringOn(sm);
140         }
141     }
142 
setUpstreamNetworkState(NetworkState ns)143     private void setUpstreamNetworkState(NetworkState ns) {
144         if (ns == null) {
145             mUpstreamNetworkState = null;
146         } else {
147             // Make a deep copy of the parts we need.
148             mUpstreamNetworkState = new NetworkState(
149                     null,
150                     new LinkProperties(ns.linkProperties),
151                     new NetworkCapabilities(ns.networkCapabilities),
152                     new Network(ns.network),
153                     null,
154                     null);
155         }
156 
157         mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
158     }
159 
updateIPv6TetheringInterfaces()160     private void updateIPv6TetheringInterfaces() {
161         for (TetherInterfaceStateMachine sm : mNotifyList) {
162             final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
163             sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
164             break;
165         }
166     }
167 
getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm)168     private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
169         if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
170             // TODO: Figure out IPv6 support on PAN interfaces.
171             return null;
172         }
173 
174         final Downstream ds = findDownstream(sm);
175         if (ds == null) return null;
176 
177         if (ds.mode == IControlsTethering.STATE_LOCAL_ONLY) {
178             // Build a Unique Locally-assigned Prefix configuration.
179             return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
180         }
181 
182         // This downstream is in IControlsTethering.STATE_TETHERED mode.
183         if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
184             return null;
185         }
186 
187         // NOTE: Here, in future, we would have policies to decide how to divvy
188         // up the available dedicated prefixes among downstream interfaces.
189         // At this time we have no such mechanism--we only support tethering
190         // IPv6 toward the oldest (first requested) active downstream.
191 
192         final Downstream currentActive = mActiveDownstreams.peek();
193         if (currentActive != null && currentActive.tism == sm) {
194             final LinkProperties lp = getIPv6OnlyLinkProperties(
195                     mUpstreamNetworkState.linkProperties);
196             if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
197                 return lp;
198             }
199         }
200 
201         return null;
202     }
203 
findDownstream(TetherInterfaceStateMachine tism)204     Downstream findDownstream(TetherInterfaceStateMachine tism) {
205         for (Downstream ds : mActiveDownstreams) {
206             if (ds.tism == tism) return ds;
207         }
208         return null;
209     }
210 
canTetherIPv6(NetworkState ns, SharedLog sharedLog)211     private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
212         // Broadly speaking:
213         //
214         //     [1] does the upstream have an IPv6 default route?
215         //
216         // and
217         //
218         //     [2] does the upstream have one or more global IPv6 /64s
219         //         dedicated to this device?
220         //
221         // In lieu of Prefix Delegation and other evaluation of whether a
222         // prefix may or may not be dedicated to this device, for now just
223         // check whether the upstream is TRANSPORT_CELLULAR. This works
224         // because "[t]he 3GPP network allocates each default bearer a unique
225         // /64 prefix", per RFC 6459, Section 5.2.
226 
227         final boolean canTether =
228                 (ns != null) && (ns.network != null) &&
229                 (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
230                 // At least one upstream DNS server:
231                 ns.linkProperties.isProvisioned() &&
232                 // Minimal amount of IPv6 provisioning:
233                 ns.linkProperties.hasIPv6DefaultRoute() &&
234                 ns.linkProperties.hasGlobalIPv6Address() &&
235                 // Temporary approximation of "dedicated prefix":
236                 ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
237 
238         // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
239         // tethering with 464xlat involved). TODO: Rectify this shortcoming,
240         // likely by calling NetworkManagementService#startInterfaceForwarding()
241         // for all upstream interfaces.
242         RouteInfo v4default = null;
243         RouteInfo v6default = null;
244         if (canTether) {
245             for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
246                 if (r.isIPv4Default()) {
247                     v4default = r;
248                 } else if (r.isIPv6Default()) {
249                     v6default = r;
250                 }
251 
252                 if (v4default != null && v6default != null) {
253                     break;
254                 }
255             }
256         }
257 
258         final boolean supportedConfiguration =
259                 (v4default != null) && (v6default != null) &&
260                 (v4default.getInterface() != null) &&
261                 v4default.getInterface().equals(v6default.getInterface());
262 
263         final boolean outcome = canTether && supportedConfiguration;
264 
265         if (ns == null) {
266             sharedLog.log("No available upstream.");
267         } else {
268             sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
269                     (outcome ? "available" : "not available"), toDebugString(ns)));
270         }
271 
272         return outcome;
273     }
274 
getIPv6OnlyLinkProperties(LinkProperties lp)275     private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
276         final LinkProperties v6only = new LinkProperties();
277         if (lp == null) {
278             return v6only;
279         }
280 
281         // NOTE: At this time we don't copy over any information about any
282         // stacked links. No current stacked link configuration has IPv6.
283 
284         v6only.setInterfaceName(lp.getInterfaceName());
285 
286         v6only.setMtu(lp.getMtu());
287 
288         for (LinkAddress linkAddr : lp.getLinkAddresses()) {
289             if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
290                 v6only.addLinkAddress(linkAddr);
291             }
292         }
293 
294         for (RouteInfo routeInfo : lp.getRoutes()) {
295             final IpPrefix destination = routeInfo.getDestination();
296             if ((destination.getAddress() instanceof Inet6Address) &&
297                 (destination.getPrefixLength() <= 64)) {
298                 v6only.addRoute(routeInfo);
299             }
300         }
301 
302         for (InetAddress dnsServer : lp.getDnsServers()) {
303             if (isIPv6GlobalAddress(dnsServer)) {
304                 // For now we include ULAs.
305                 v6only.addDnsServer(dnsServer);
306             }
307         }
308 
309         v6only.setDomains(lp.getDomains());
310 
311         return v6only;
312     }
313 
314     // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
315     // announce our own IPv6 address as DNS server.
isIPv6GlobalAddress(InetAddress ip)316     private static boolean isIPv6GlobalAddress(InetAddress ip) {
317         return (ip instanceof Inet6Address) &&
318                !ip.isAnyLocalAddress() &&
319                !ip.isLoopbackAddress() &&
320                !ip.isLinkLocalAddress() &&
321                !ip.isSiteLocalAddress() &&
322                !ip.isMulticastAddress();
323     }
324 
getUniqueLocalConfig(byte[] ulp, short subnetId)325     private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) {
326         final LinkProperties lp = new LinkProperties();
327 
328         final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
329         lp.addRoute(new RouteInfo(local48, null, null));
330 
331         final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
332         // Because this is a locally-generated ULA, we don't have an upstream
333         // address. But because the downstream IP address management code gets
334         // its prefix from the upstream's IP address, we create a fake one here.
335         lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64));
336 
337         lp.setMtu(NetworkConstants.ETHER_MTU);
338         return lp;
339     }
340 
makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen)341     private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) {
342         final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
343         bytes[7] = (byte) (subnetId >> 8);
344         bytes[8] = (byte) subnetId;
345         return new IpPrefix(bytes, prefixlen);
346     }
347 
348     // Generates a Unique Locally-assigned Prefix:
349     //
350     //     https://tools.ietf.org/html/rfc4193#section-3.1
351     //
352     // The result is a /48 that can be used for local-only communications.
generateUniqueLocalPrefix()353     private static byte[] generateUniqueLocalPrefix() {
354         final byte[] ulp = new byte[6];  // 6 = 48bits / 8bits/byte
355         (new Random()).nextBytes(ulp);
356 
357         final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN);
358         in6addr[0] = (byte) 0xfd;  // fc00::/7 and L=1
359 
360         return in6addr;
361     }
362 
toDebugString(NetworkState ns)363     private static String toDebugString(NetworkState ns) {
364         if (ns == null) {
365             return "NetworkState{null}";
366         }
367         return String.format("NetworkState{%s, %s, %s}",
368                 ns.network,
369                 ns.networkCapabilities,
370                 ns.linkProperties);
371     }
372 
stopIPv6TetheringOn(TetherInterfaceStateMachine sm)373     private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
374         sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
375     }
376 }
377