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