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