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