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.networkstack.tethering;
18 
19 import android.net.IpPrefix;
20 import android.net.LinkAddress;
21 import android.net.LinkProperties;
22 import android.net.Network;
23 import android.net.NetworkCapabilities;
24 import android.net.RouteInfo;
25 import android.net.ip.IpServer;
26 import android.net.util.NetworkConstants;
27 import android.net.util.SharedLog;
28 import android.util.Log;
29 
30 import java.net.Inet6Address;
31 import java.net.InetAddress;
32 import java.net.UnknownHostException;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.LinkedList;
36 import java.util.Random;
37 
38 
39 /**
40  * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
41  * This coordinator is responsible for evaluating the dedicated prefixes
42  * assigned to the device and deciding how to divvy them up among downstream
43  * interfaces.
44  *
45  * @hide
46  */
47 public class IPv6TetheringCoordinator {
48     private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
49     private static final boolean DBG = false;
50     private static final boolean VDBG = false;
51 
52     private static class Downstream {
53         public final IpServer ipServer;
54         public final int mode;  // IpServer.STATE_*
55         // Used to append to a ULA /48, constructing a ULA /64 for local use.
56         public final short subnetId;
57 
Downstream(IpServer ipServer, int mode, short subnetId)58         Downstream(IpServer ipServer, int mode, short subnetId) {
59             this.ipServer = ipServer;
60             this.mode = mode;
61             this.subnetId = subnetId;
62         }
63     }
64 
65     private final ArrayList<IpServer> mNotifyList;
66     private final SharedLog mLog;
67     // NOTE: mActiveDownstreams is a list and not a hash data structure because
68     // we keep active downstreams in arrival order.  This is done so /64s can
69     // be parceled out on a "first come, first served" basis and a /64 used by
70     // a downstream that is no longer active can be redistributed to any next
71     // waiting active downstream (again, in arrival order).
72     private final LinkedList<Downstream> mActiveDownstreams;
73     private final byte[] mUniqueLocalPrefix;
74     private short mNextSubnetId;
75     private UpstreamNetworkState mUpstreamNetworkState;
76 
IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log)77     public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
78         mNotifyList = notifyList;
79         mLog = log.forSubComponent(TAG);
80         mActiveDownstreams = new LinkedList<>();
81         mUniqueLocalPrefix = generateUniqueLocalPrefix();
82         mNextSubnetId = 0;
83     }
84 
85     /** Add active downstream to ipv6 tethering candidate list. */
addActiveDownstream(IpServer downstream, int mode)86     public void addActiveDownstream(IpServer downstream, int mode) {
87         if (findDownstream(downstream) == null) {
88             // Adding a new downstream appends it to the list. Adding a
89             // downstream a second time without first removing it has no effect.
90             // We never change the mode of a downstream except by first removing
91             // it and then re-adding it (with its new mode specified);
92             if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) {
93                 // Make sure subnet IDs are always positive. They are appended
94                 // to a ULA /48 to make a ULA /64 for local use.
95                 mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1);
96             }
97             updateIPv6TetheringInterfaces();
98         }
99     }
100 
101     /** Remove downstream from ipv6 tethering candidate list. */
removeActiveDownstream(IpServer downstream)102     public void removeActiveDownstream(IpServer downstream) {
103         stopIPv6TetheringOn(downstream);
104         if (mActiveDownstreams.remove(findDownstream(downstream))) {
105             updateIPv6TetheringInterfaces();
106         }
107 
108         // When tethering is stopping we can reset the subnet counter.
109         if (mNotifyList.isEmpty()) {
110             if (!mActiveDownstreams.isEmpty()) {
111                 Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty.");
112             }
113             mNextSubnetId = 0;
114         }
115     }
116 
117     /**
118      * Call when UpstreamNetworkState may be changed.
119      * If upstream has ipv6 for tethering, update this new UpstreamNetworkState
120      * to IpServer. Otherwise stop ipv6 tethering on downstream interfaces.
121      */
updateUpstreamNetworkState(UpstreamNetworkState ns)122     public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
123         if (VDBG) {
124             Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
125         }
126         if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
127             stopIPv6TetheringOnAllInterfaces();
128             setUpstreamNetworkState(null);
129             return;
130         }
131 
132         if (mUpstreamNetworkState != null
133                 && !ns.network.equals(mUpstreamNetworkState.network)) {
134             stopIPv6TetheringOnAllInterfaces();
135         }
136 
137         setUpstreamNetworkState(ns);
138         updateIPv6TetheringInterfaces();
139     }
140 
stopIPv6TetheringOnAllInterfaces()141     private void stopIPv6TetheringOnAllInterfaces() {
142         for (IpServer ipServer : mNotifyList) {
143             stopIPv6TetheringOn(ipServer);
144         }
145     }
146 
setUpstreamNetworkState(UpstreamNetworkState ns)147     private void setUpstreamNetworkState(UpstreamNetworkState ns) {
148         if (ns == null) {
149             mUpstreamNetworkState = null;
150         } else {
151             // Make a deep copy of the parts we need.
152             mUpstreamNetworkState = new UpstreamNetworkState(
153                     new LinkProperties(ns.linkProperties),
154                     new NetworkCapabilities(ns.networkCapabilities),
155                     new Network(ns.network));
156         }
157 
158         mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
159     }
160 
updateIPv6TetheringInterfaces()161     private void updateIPv6TetheringInterfaces() {
162         for (IpServer ipServer : mNotifyList) {
163             final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
164             ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp);
165             break;
166         }
167     }
168 
getTtlAdjustment()169     private int getTtlAdjustment() {
170         if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) {
171             return 0;
172         }
173 
174         // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1
175         // for carrier requirement.
176         if (mUpstreamNetworkState.networkCapabilities.hasTransport(
177                 NetworkCapabilities.TRANSPORT_CELLULAR)) {
178             return -1;
179         }
180 
181         // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary
182         // distinction between tethered and untethered traffic.
183         return 1;
184     }
185 
getInterfaceIPv6LinkProperties(IpServer ipServer)186     private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
187         final Downstream ds = findDownstream(ipServer);
188         if (ds == null) return null;
189 
190         if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
191             // Build a Unique Locally-assigned Prefix configuration.
192             return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
193         }
194 
195         // This downstream is in IpServer.STATE_TETHERED mode.
196         if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
197             return null;
198         }
199 
200         // NOTE: Here, in future, we would have policies to decide how to divvy
201         // up the available dedicated prefixes among downstream interfaces.
202         // At this time we have no such mechanism--we only support tethering
203         // IPv6 toward the oldest (first requested) active downstream.
204 
205         final Downstream currentActive = mActiveDownstreams.peek();
206         if (currentActive != null && currentActive.ipServer == ipServer) {
207             final LinkProperties lp = getIPv6OnlyLinkProperties(
208                     mUpstreamNetworkState.linkProperties);
209             if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) {
210                 return lp;
211             }
212         }
213 
214         return null;
215     }
216 
findDownstream(IpServer ipServer)217     Downstream findDownstream(IpServer ipServer) {
218         for (Downstream ds : mActiveDownstreams) {
219             if (ds.ipServer == ipServer) return ds;
220         }
221         return null;
222     }
223 
getIPv6OnlyLinkProperties(LinkProperties lp)224     private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
225         final LinkProperties v6only = new LinkProperties();
226         if (lp == null) {
227             return v6only;
228         }
229 
230         // NOTE: At this time we don't copy over any information about any
231         // stacked links. No current stacked link configuration has IPv6.
232 
233         v6only.setInterfaceName(lp.getInterfaceName());
234 
235         v6only.setMtu(lp.getMtu());
236 
237         for (LinkAddress linkAddr : lp.getLinkAddresses()) {
238             if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
239                 v6only.addLinkAddress(linkAddr);
240             }
241         }
242 
243         for (RouteInfo routeInfo : lp.getRoutes()) {
244             final IpPrefix destination = routeInfo.getDestination();
245             if ((destination.getAddress() instanceof Inet6Address)
246                     && (destination.getPrefixLength() <= 64)) {
247                 v6only.addRoute(routeInfo);
248             }
249         }
250 
251         for (InetAddress dnsServer : lp.getDnsServers()) {
252             if (isIPv6GlobalAddress(dnsServer)) {
253                 // For now we include ULAs.
254                 v6only.addDnsServer(dnsServer);
255             }
256         }
257 
258         v6only.setDomains(lp.getDomains());
259 
260         return v6only;
261     }
262 
263     // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
264     // announce our own IPv6 address as DNS server.
isIPv6GlobalAddress(InetAddress ip)265     private static boolean isIPv6GlobalAddress(InetAddress ip) {
266         return (ip instanceof Inet6Address)
267                && !ip.isAnyLocalAddress()
268                && !ip.isLoopbackAddress()
269                && !ip.isLinkLocalAddress()
270                && !ip.isSiteLocalAddress()
271                && !ip.isMulticastAddress();
272     }
273 
getUniqueLocalConfig(byte[] ulp, short subnetId)274     private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) {
275         final LinkProperties lp = new LinkProperties();
276 
277         final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
278         lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST));
279 
280         final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
281         // Because this is a locally-generated ULA, we don't have an upstream
282         // address. But because the downstream IP address management code gets
283         // its prefix from the upstream's IP address, we create a fake one here.
284         lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64));
285 
286         lp.setMtu(NetworkConstants.ETHER_MTU);
287         return lp;
288     }
289 
makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen)290     private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) {
291         final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
292         bytes[7] = (byte) (subnetId >> 8);
293         bytes[8] = (byte) subnetId;
294         final InetAddress addr;
295         try {
296             addr = InetAddress.getByAddress(bytes);
297         } catch (UnknownHostException e) {
298             throw new IllegalStateException("Invalid address length: " + bytes.length, e);
299         }
300         return new IpPrefix(addr, prefixlen);
301     }
302 
303     // Generates a Unique Locally-assigned Prefix:
304     //
305     //     https://tools.ietf.org/html/rfc4193#section-3.1
306     //
307     // The result is a /48 that can be used for local-only communications.
generateUniqueLocalPrefix()308     private static byte[] generateUniqueLocalPrefix() {
309         final byte[] ulp = new byte[6];  // 6 = 48bits / 8bits/byte
310         (new Random()).nextBytes(ulp);
311 
312         final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN);
313         in6addr[0] = (byte) 0xfd;  // fc00::/7 and L=1
314 
315         return in6addr;
316     }
317 
toDebugString(UpstreamNetworkState ns)318     private static String toDebugString(UpstreamNetworkState ns) {
319         if (ns == null) {
320             return "UpstreamNetworkState{null}";
321         }
322         return ns.toString();
323     }
324 
stopIPv6TetheringOn(IpServer ipServer)325     private static void stopIPv6TetheringOn(IpServer ipServer) {
326         ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
327     }
328 }
329