1 /*
2  * Copyright (C) 2020 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 package com.android.networkstack.tethering;
17 
18 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
19 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
20 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
21 import static android.net.util.PrefixUtils.asIpPrefix;
22 
23 import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
24 import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
25 import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
26 
27 import static java.util.Arrays.asList;
28 
29 import android.content.Context;
30 import android.net.ConnectivityManager;
31 import android.net.IpPrefix;
32 import android.net.LinkAddress;
33 import android.net.Network;
34 import android.net.ip.IpServer;
35 import android.util.ArrayMap;
36 import android.util.ArraySet;
37 import android.util.SparseArray;
38 
39 import androidx.annotation.NonNull;
40 import androidx.annotation.Nullable;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.IndentingPrintWriter;
44 
45 import java.net.Inet4Address;
46 import java.net.InetAddress;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Random;
52 import java.util.Set;
53 
54 /**
55  * This class coordinate IP addresses conflict problem.
56  *
57  * Tethering downstream IP addresses may conflict with network assigned addresses. This
58  * coordinator is responsible for recording all of network assigned addresses and dispatched
59  * free address to downstream interfaces.
60  *
61  * This class is not thread-safe and should be accessed on the same tethering internal thread.
62  * @hide
63  */
64 public class PrivateAddressCoordinator {
65     public static final int PREFIX_LENGTH = 24;
66 
67     // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
68     // address may be requested before coordinator get current upstream notification. To ensure
69     // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
70     // when tethering is down. Instead tethering would remove all deprecated upstreams from
71     // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
72     private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
73     private final ArraySet<IpServer> mDownstreams;
74     private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
75     private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
76     private final List<IpPrefix> mTetheringPrefixes;
77     private final ConnectivityManager mConnectivityMgr;
78     private final TetheringConfiguration mConfig;
79     // keyed by downstream type(TetheringManager.TETHERING_*).
80     private final SparseArray<LinkAddress> mCachedAddresses;
81 
PrivateAddressCoordinator(Context context, TetheringConfiguration config)82     public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
83         mDownstreams = new ArraySet<>();
84         mUpstreamPrefixMap = new ArrayMap<>();
85         mConnectivityMgr = (ConnectivityManager) context.getSystemService(
86                 Context.CONNECTIVITY_SERVICE);
87         mConfig = config;
88         mCachedAddresses = new SparseArray<>();
89         // Reserved static addresses for bluetooth and wifi p2p.
90         mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
91         mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
92 
93         mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16")));
94         if (config.isSelectAllPrefixRangeEnabled()) {
95             mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12"));
96             mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8"));
97         }
98     }
99 
100     /**
101      * Record a new upstream IpPrefix which may conflict with tethering downstreams.
102      * The downstreams will be notified if a conflict is found. When updateUpstreamPrefix is called,
103      * UpstreamNetworkState must have an already populated LinkProperties.
104      */
updateUpstreamPrefix(final UpstreamNetworkState ns)105     public void updateUpstreamPrefix(final UpstreamNetworkState ns) {
106         // Do not support VPN as upstream. Normally, networkCapabilities is not expected to be null,
107         // but just checking to be sure.
108         if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
109             removeUpstreamPrefix(ns.network);
110             return;
111         }
112 
113         final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(
114                 ns.linkProperties.getAllLinkAddresses());
115         if (ipv4Prefixes.isEmpty()) {
116             removeUpstreamPrefix(ns.network);
117             return;
118         }
119 
120         mUpstreamPrefixMap.put(ns.network, ipv4Prefixes);
121         handleMaybePrefixConflict(ipv4Prefixes);
122     }
123 
getIpv4Prefixes(final List<LinkAddress> linkAddresses)124     private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) {
125         final ArrayList<IpPrefix> list = new ArrayList<>();
126         for (LinkAddress address : linkAddresses) {
127             if (!address.isIpv4()) continue;
128 
129             list.add(asIpPrefix(address));
130         }
131 
132         return list;
133     }
134 
handleMaybePrefixConflict(final List<IpPrefix> prefixes)135     private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
136         for (IpServer downstream : mDownstreams) {
137             final IpPrefix target = getDownstreamPrefix(downstream);
138 
139             for (IpPrefix source : prefixes) {
140                 if (isConflictPrefix(source, target)) {
141                     downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
142                     break;
143                 }
144             }
145         }
146     }
147 
148     /** Remove IpPrefix records corresponding to input network. */
removeUpstreamPrefix(final Network network)149     public void removeUpstreamPrefix(final Network network) {
150         mUpstreamPrefixMap.remove(network);
151     }
152 
153     /**
154      * Maybe remove deprecated upstream records, this would be called once tethering started without
155      * any exiting tethered downstream.
156      */
maybeRemoveDeprecatedUpstreams()157     public void maybeRemoveDeprecatedUpstreams() {
158         if (mUpstreamPrefixMap.isEmpty()) return;
159 
160         // Remove all upstreams that are no longer valid networks
161         final Set<Network> toBeRemoved = new HashSet<>(mUpstreamPrefixMap.keySet());
162         toBeRemoved.removeAll(asList(mConnectivityMgr.getAllNetworks()));
163 
164         mUpstreamPrefixMap.removeAll(toBeRemoved);
165     }
166 
167     /**
168      * Pick a random available address and mark its prefix as in use for the provided IpServer,
169      * returns null if there is no available address.
170      */
171     @Nullable
requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress)172     public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
173         if (mConfig.shouldEnableWifiP2pDedicatedIp()
174                 && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
175             return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
176         }
177 
178         final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
179         if (useLastAddress && cachedAddress != null
180                 && !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
181             mDownstreams.add(ipServer);
182             return cachedAddress;
183         }
184 
185         for (IpPrefix prefixRange : mTetheringPrefixes) {
186             final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
187             if (newAddress != null) {
188                 mDownstreams.add(ipServer);
189                 mCachedAddresses.put(ipServer.interfaceType(), newAddress);
190                 return newAddress;
191             }
192         }
193 
194         // No available address.
195         return null;
196     }
197 
getPrefixBaseAddress(final IpPrefix prefix)198     private int getPrefixBaseAddress(final IpPrefix prefix) {
199         return inet4AddressToIntHTH((Inet4Address) prefix.getAddress());
200     }
201 
202     /**
203      * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes.
204      * If yes, return one of them.
205      */
getConflictPrefix(final IpPrefix prefix)206     private IpPrefix getConflictPrefix(final IpPrefix prefix) {
207         final IpPrefix upstream = getConflictWithUpstream(prefix);
208         if (upstream != null) return upstream;
209 
210         return getInUseDownstreamPrefix(prefix);
211     }
212 
213     // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the
214     // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix
215     // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is
216     // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0).
217     // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as
218     // selected random sub address later.
getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask)219     private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) {
220         final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength());
221         // The largest offset within the prefix assignment block that still conflicts with
222         // conflictPrefix.
223         final int maxConflict =
224                 (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask;
225 
226         final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
227         // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than
228         // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix.
229         // There is no need to mask the result with PREFIX_LENGTH bits because this is done by
230         // findAvailablePrefixFromRange when it constructs the prefix.
231         return maxConflict + (1 << (32 - PREFIX_LENGTH));
232     }
233 
chooseDownstreamAddress(final IpPrefix prefixRange)234     private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) {
235         // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12).
236         final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength());
237 
238         // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12).
239         final int baseAddress = getPrefixBaseAddress(prefixRange);
240 
241         // The subnet mask corresponding to PREFIX_LENGTH.
242         final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
243 
244         // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH.
245         // This may not be the prefix of the address returned by this method:
246         // - If it is already in use, the method will return an address in another prefix.
247         // - If all prefixes within prefixRange are in use, the method will return null. For
248         // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in
249         // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00.
250         //
251         // prefixRangeMask is required to ensure no wrapping. For example, consider:
252         // - prefixRange 127.0.0.0/8
253         // - randomPrefixStart 127.255.255.0
254         // - A conflicting prefix of 127.255.254.0/23
255         // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which
256         // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix
257         // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648
258         // is less than 127.0.0.0 = 0x7f000000 = 2130706432.
259         //
260         // Additionally, it makes debug output easier to read by making the numbers smaller.
261         final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask;
262 
263         // A random offset within the prefix. Used to determine the local address once the prefix
264         // is selected. It does not result in an IPv4 address ending in .0, .1, or .255
265         // For a PREFIX_LENGTH of 255, this is a number between 2 and 254.
266         final int subAddress = getSanitizedSubAddr(~prefixMask);
267 
268         // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block,
269         // such that the prefix does not conflict with any upstream.
270         IpPrefix downstreamPrefix = findAvailablePrefixFromRange(
271                  randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask);
272         if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress);
273 
274         // If that failed, do the same, but between 0 and randomPrefixStart.
275         downstreamPrefix = findAvailablePrefixFromRange(
276                 0, randomPrefixStart, baseAddress, prefixRangeMask);
277 
278         return getLinkAddress(downstreamPrefix, subAddress);
279     }
280 
getLinkAddress(final IpPrefix prefix, final int subAddress)281     private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) {
282         if (prefix == null) return null;
283 
284         final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress);
285         return new LinkAddress(address, PREFIX_LENGTH);
286     }
287 
findAvailablePrefixFromRange(final int start, final int end, final int baseAddress, final int prefixRangeMask)288     private IpPrefix findAvailablePrefixFromRange(final int start, final int end,
289             final int baseAddress, final int prefixRangeMask) {
290         int newSubPrefix = start;
291         while (newSubPrefix < end) {
292             final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix);
293             final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH);
294 
295             final IpPrefix conflictPrefix = getConflictPrefix(prefix);
296 
297             if (conflictPrefix == null) return prefix;
298 
299             newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask);
300         }
301 
302         return null;
303     }
304 
305     /** Get random int which could be used to generate random address. */
306     @VisibleForTesting
getRandomInt()307     public int getRandomInt() {
308         return (new Random()).nextInt();
309     }
310 
311     /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */
getSanitizedSubAddr(final int subAddrMask)312     private int getSanitizedSubAddr(final int subAddrMask) {
313         final int randomSubAddr = getRandomInt() & subAddrMask;
314         // If prefix length > 30, the selecting speace would be less than 4 which may be hard to
315         // avoid 3 consecutive address.
316         if (PREFIX_LENGTH > 30) return randomSubAddr;
317 
318         // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering
319         // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer
320         // than 24
321         final int candidate = randomSubAddr & 0xff;
322         if (candidate == 0 || candidate == 1 || candidate == 255) {
323             return (randomSubAddr & 0xfffffffc) + 2;
324         }
325 
326         return randomSubAddr;
327     }
328 
329     /** Release downstream record for IpServer. */
releaseDownstream(final IpServer ipServer)330     public void releaseDownstream(final IpServer ipServer) {
331         mDownstreams.remove(ipServer);
332     }
333 
334     /** Clear current upstream prefixes records. */
clearUpstreamPrefixes()335     public void clearUpstreamPrefixes() {
336         mUpstreamPrefixMap.clear();
337     }
338 
getConflictWithUpstream(final IpPrefix prefix)339     private IpPrefix getConflictWithUpstream(final IpPrefix prefix) {
340         for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
341             final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
342             for (IpPrefix upstream : list) {
343                 if (isConflictPrefix(prefix, upstream)) return upstream;
344             }
345         }
346         return null;
347     }
348 
isConflictWithUpstream(final IpPrefix prefix)349     private boolean isConflictWithUpstream(final IpPrefix prefix) {
350         return getConflictWithUpstream(prefix) != null;
351     }
352 
isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2)353     private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
354         if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) {
355             return prefix2.contains(prefix1.getAddress());
356         }
357 
358         return prefix1.contains(prefix2.getAddress());
359     }
360 
361     // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last
362     // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p).
getInUseDownstreamPrefix(final IpPrefix prefix)363     private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) {
364         for (int i = 0; i < mCachedAddresses.size(); i++) {
365             final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i));
366             if (isConflictPrefix(prefix, downstream)) return downstream;
367         }
368 
369         // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
370         // in mCachedAddresses.
371         for (IpServer downstream : mDownstreams) {
372             final IpPrefix target = getDownstreamPrefix(downstream);
373 
374             if (isConflictPrefix(prefix, target)) return target;
375         }
376 
377         return null;
378     }
379 
380     @NonNull
getDownstreamPrefix(final IpServer downstream)381     private IpPrefix getDownstreamPrefix(final IpServer downstream) {
382         final LinkAddress address = downstream.getAddress();
383 
384         return asIpPrefix(address);
385     }
386 
dump(final IndentingPrintWriter pw)387     void dump(final IndentingPrintWriter pw) {
388         pw.println("mTetheringPrefixes:");
389         pw.increaseIndent();
390         for (IpPrefix prefix : mTetheringPrefixes) {
391             pw.println(prefix);
392         }
393         pw.decreaseIndent();
394 
395         pw.println("mUpstreamPrefixMap:");
396         pw.increaseIndent();
397         for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
398             pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
399         }
400         pw.decreaseIndent();
401 
402         pw.println("mDownstreams:");
403         pw.increaseIndent();
404         for (IpServer ipServer : mDownstreams) {
405             pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
406         }
407         pw.decreaseIndent();
408 
409         pw.println("mCachedAddresses:");
410         pw.increaseIndent();
411         for (int i = 0; i < mCachedAddresses.size(); i++) {
412             pw.println(mCachedAddresses.keyAt(i) + " - " + mCachedAddresses.valueAt(i));
413         }
414         pw.decreaseIndent();
415     }
416 }
417