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