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