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 android.content.Context; 19 import android.net.ConnectivityManager; 20 import android.net.IpPrefix; 21 import android.net.LinkAddress; 22 import android.net.LinkProperties; 23 import android.net.Network; 24 import android.net.ip.IpServer; 25 import android.net.util.PrefixUtils; 26 import android.util.ArrayMap; 27 import android.util.ArraySet; 28 29 import androidx.annotation.Nullable; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.util.IndentingPrintWriter; 33 34 import java.net.InetAddress; 35 import java.net.UnknownHostException; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.Random; 40 41 /** 42 * This class coordinate IP addresses conflict problem. 43 * 44 * Tethering downstream IP addresses may conflict with network assigned addresses. This 45 * coordinator is responsible for recording all of network assigned addresses and dispatched 46 * free address to downstream interfaces. 47 * 48 * This class is not thread-safe and should be accessed on the same tethering internal thread. 49 * @hide 50 */ 51 public class PrivateAddressCoordinator { 52 public static final int PREFIX_LENGTH = 24; 53 54 private static final int MAX_UBYTE = 256; 55 private static final int BYTE_MASK = 0xff; 56 // reserved for bluetooth tethering. 57 private static final int BLUETOOTH_RESERVED = 44; 58 private static final byte DEFAULT_ID = (byte) 42; 59 60 // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream 61 // address may be requested before coordinator get current upstream notification. To ensure 62 // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared 63 // when tethering is down. Instead coordinator would remove all depcreted upstreams from 64 // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprectedUpstreams(). 65 private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap; 66 private final ArraySet<IpServer> mDownstreams; 67 // IANA has reserved the following three blocks of the IP address space for private intranets: 68 // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 69 // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers. 70 private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16"; 71 private final IpPrefix mTetheringPrefix; 72 private final ConnectivityManager mConnectivityMgr; 73 PrivateAddressCoordinator(Context context)74 public PrivateAddressCoordinator(Context context) { 75 mDownstreams = new ArraySet<>(); 76 mUpstreamPrefixMap = new ArrayMap<>(); 77 mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX); 78 mConnectivityMgr = (ConnectivityManager) context.getSystemService( 79 Context.CONNECTIVITY_SERVICE); 80 } 81 82 /** 83 * Record a new upstream IpPrefix which may conflict with tethering downstreams. 84 * The downstreams will be notified if a conflict is found. 85 */ updateUpstreamPrefix(final Network network, final LinkProperties lp)86 public void updateUpstreamPrefix(final Network network, final LinkProperties lp) { 87 final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses()); 88 if (ipv4Prefixes.isEmpty()) { 89 removeUpstreamPrefix(network); 90 return; 91 } 92 93 mUpstreamPrefixMap.put(network, ipv4Prefixes); 94 handleMaybePrefixConflict(ipv4Prefixes); 95 } 96 getIpv4Prefixes(final List<LinkAddress> linkAddresses)97 private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) { 98 final ArrayList<IpPrefix> list = new ArrayList<>(); 99 for (LinkAddress address : linkAddresses) { 100 if (!address.isIpv4()) continue; 101 102 list.add(PrefixUtils.asIpPrefix(address)); 103 } 104 105 return list; 106 } 107 handleMaybePrefixConflict(final List<IpPrefix> prefixes)108 private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) { 109 for (IpServer downstream : mDownstreams) { 110 final IpPrefix target = getDownstreamPrefix(downstream); 111 if (target == null) continue; 112 113 for (IpPrefix source : prefixes) { 114 if (isConflictPrefix(source, target)) { 115 downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); 116 break; 117 } 118 } 119 } 120 } 121 122 /** Remove IpPrefix records corresponding to input network. */ removeUpstreamPrefix(final Network network)123 public void removeUpstreamPrefix(final Network network) { 124 mUpstreamPrefixMap.remove(network); 125 } 126 maybeRemoveDeprectedUpstreams()127 private void maybeRemoveDeprectedUpstreams() { 128 if (!mDownstreams.isEmpty() || mUpstreamPrefixMap.isEmpty()) return; 129 130 final ArrayList<Network> toBeRemoved = new ArrayList<>(); 131 List<Network> allNetworks = Arrays.asList(mConnectivityMgr.getAllNetworks()); 132 for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { 133 final Network network = mUpstreamPrefixMap.keyAt(i); 134 if (!allNetworks.contains(network)) toBeRemoved.add(network); 135 } 136 137 mUpstreamPrefixMap.removeAll(toBeRemoved); 138 } 139 140 /** 141 * Pick a random available address and mark its prefix as in use for the provided IpServer, 142 * returns null if there is no available address. 143 */ 144 @Nullable requestDownstreamAddress(final IpServer ipServer)145 public LinkAddress requestDownstreamAddress(final IpServer ipServer) { 146 maybeRemoveDeprectedUpstreams(); 147 148 // Address would be 192.168.[subAddress]/24. 149 final byte[] bytes = mTetheringPrefix.getRawAddress(); 150 final int subAddress = getRandomSubAddr(); 151 final int subNet = (subAddress >> 8) & BYTE_MASK; 152 bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff); 153 for (int i = 0; i < MAX_UBYTE; i++) { 154 final int newSubNet = (subNet + i) & BYTE_MASK; 155 if (newSubNet == BLUETOOTH_RESERVED) continue; 156 157 bytes[2] = (byte) newSubNet; 158 final InetAddress addr; 159 try { 160 addr = InetAddress.getByAddress(bytes); 161 } catch (UnknownHostException e) { 162 throw new IllegalStateException("Invalid address, shouldn't happen.", e); 163 } 164 165 final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH); 166 // Check whether this prefix is in use. 167 if (isDownstreamPrefixInUse(prefix)) continue; 168 // Check whether this prefix is conflict with any current upstream network. 169 if (isConflictWithUpstream(prefix)) continue; 170 171 mDownstreams.add(ipServer); 172 return new LinkAddress(addr, PREFIX_LENGTH); 173 } 174 175 // No available address. 176 return null; 177 } 178 179 /** Get random sub address value. Return value is in 0 ~ 0xffff. */ 180 @VisibleForTesting getRandomSubAddr()181 public int getRandomSubAddr() { 182 return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff. 183 } 184 getSanitizedAddressSuffix(final int source, byte... excluded)185 private byte getSanitizedAddressSuffix(final int source, byte... excluded) { 186 final byte subId = (byte) (source & BYTE_MASK); 187 for (byte value : excluded) { 188 if (subId == value) return DEFAULT_ID; 189 } 190 191 return subId; 192 } 193 194 /** Release downstream record for IpServer. */ releaseDownstream(final IpServer ipServer)195 public void releaseDownstream(final IpServer ipServer) { 196 mDownstreams.remove(ipServer); 197 } 198 199 /** Clear current upstream prefixes records. */ clearUpstreamPrefixes()200 public void clearUpstreamPrefixes() { 201 mUpstreamPrefixMap.clear(); 202 } 203 isConflictWithUpstream(final IpPrefix source)204 private boolean isConflictWithUpstream(final IpPrefix source) { 205 for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { 206 final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i); 207 for (IpPrefix target : list) { 208 if (isConflictPrefix(source, target)) return true; 209 } 210 } 211 return false; 212 } 213 isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2)214 private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) { 215 if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) { 216 return prefix2.contains(prefix1.getAddress()); 217 } 218 219 return prefix1.contains(prefix2.getAddress()); 220 } 221 isDownstreamPrefixInUse(final IpPrefix source)222 private boolean isDownstreamPrefixInUse(final IpPrefix source) { 223 // This class always generates downstream prefixes with the same prefix length, so 224 // prefixes cannot be contained in each other. They can only be equal to each other. 225 for (IpServer downstream : mDownstreams) { 226 final IpPrefix prefix = getDownstreamPrefix(downstream); 227 if (source.equals(prefix)) return true; 228 } 229 return false; 230 } 231 getDownstreamPrefix(final IpServer downstream)232 private IpPrefix getDownstreamPrefix(final IpServer downstream) { 233 final LinkAddress address = downstream.getAddress(); 234 if (address == null) return null; 235 236 return PrefixUtils.asIpPrefix(address); 237 } 238 dump(final IndentingPrintWriter pw)239 void dump(final IndentingPrintWriter pw) { 240 pw.decreaseIndent(); 241 pw.println("mUpstreamPrefixMap:"); 242 pw.increaseIndent(); 243 for (int i = 0; i < mUpstreamPrefixMap.size(); i++) { 244 pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i)); 245 } 246 pw.decreaseIndent(); 247 pw.println("mDownstreams:"); 248 pw.increaseIndent(); 249 for (IpServer ipServer : mDownstreams) { 250 pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress()); 251 } 252 pw.decreaseIndent(); 253 } 254 } 255