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