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 org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertNotEquals;
20 import static org.mockito.Mockito.never;
21 import static org.mockito.Mockito.reset;
22 import static org.mockito.Mockito.spy;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 
26 import android.content.Context;
27 import android.net.ConnectivityManager;
28 import android.net.InetAddresses;
29 import android.net.IpPrefix;
30 import android.net.LinkAddress;
31 import android.net.LinkProperties;
32 import android.net.Network;
33 import android.net.ip.IpServer;
34 import android.net.util.NetworkConstants;
35 import android.net.util.PrefixUtils;
36 
37 import androidx.test.filters.SmallTest;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.mockito.Mock;
44 import org.mockito.MockitoAnnotations;
45 
46 import java.util.List;
47 
48 @RunWith(AndroidJUnit4.class)
49 @SmallTest
50 public final class PrivateAddressCoordinatorTest {
51     private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
52     private static final String TEST_WIFI_IFNAME = "test_wlan0";
53 
54     @Mock private IpServer mHotspotIpServer;
55     @Mock private IpServer mUsbIpServer;
56     @Mock private IpServer mEthernetIpServer;
57     @Mock private Context mContext;
58     @Mock private ConnectivityManager mConnectivityMgr;
59 
60     private PrivateAddressCoordinator mPrivateAddressCoordinator;
61     private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
62     private final Network mWifiNetwork = new Network(1);
63     private final Network mMobileNetwork = new Network(2);
64     private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
65 
66     @Before
setUp()67     public void setUp() throws Exception {
68         MockitoAnnotations.initMocks(this);
69 
70         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
71         when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
72         mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext));
73     }
74 
75     @Test
testDownstreamPrefixRequest()76     public void testDownstreamPrefixRequest() throws Exception {
77         LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
78                 mHotspotIpServer);
79         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
80         assertNotEquals(hotspotPrefix, mBluetoothPrefix);
81 
82         address = mPrivateAddressCoordinator.requestDownstreamAddress(
83                 mHotspotIpServer);
84         final IpPrefix testDupRequest = PrefixUtils.asIpPrefix(address);
85         assertNotEquals(hotspotPrefix, testDupRequest);
86         assertNotEquals(mBluetoothPrefix, testDupRequest);
87         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
88 
89         address = mPrivateAddressCoordinator.requestDownstreamAddress(
90                 mUsbIpServer);
91         final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
92         assertNotEquals(usbPrefix, mBluetoothPrefix);
93         assertNotEquals(usbPrefix, hotspotPrefix);
94         mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
95     }
96 
97     @Test
testRequestDownstreamAddress()98     public void testRequestDownstreamAddress() throws Exception {
99         LinkAddress expectedAddress = new LinkAddress("192.168.43.42/24");
100         int fakeSubAddr = 0x2b00;
101         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
102         LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
103                 mHotspotIpServer);
104         assertEquals(actualAddress, expectedAddress);
105         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
106 
107         fakeSubAddr = 0x2b01;
108         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
109         actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
110                 mHotspotIpServer);
111         assertEquals(actualAddress, expectedAddress);
112         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
113 
114         fakeSubAddr = 0x2bff;
115         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
116         actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
117                 mHotspotIpServer);
118         assertEquals(actualAddress, expectedAddress);
119         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
120 
121         expectedAddress = new LinkAddress("192.168.43.5/24");
122         fakeSubAddr = 0x2b05;
123         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
124         actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
125                 mHotspotIpServer);
126         assertEquals(actualAddress, expectedAddress);
127         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
128     }
129 
getBluetoothSubAddress()130     private int getBluetoothSubAddress() {
131         final byte[] rawAddress = mBluetoothPrefix.getRawAddress();
132         int bluetoothSubNet = rawAddress[2] & 0xff;
133         return (bluetoothSubNet << 8) + 0x5;
134     }
135 
136     @Test
testReserveBluetoothPrefix()137     public void testReserveBluetoothPrefix() throws Exception {
138         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(getBluetoothSubAddress());
139         LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
140                 mHotspotIpServer);
141         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
142         assertNotEquals("Should not get reserved prefix: ", mBluetoothPrefix, hotspotPrefix);
143         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
144     }
145 
146     @Test
testNoConflictDownstreamPrefix()147     public void testNoConflictDownstreamPrefix() throws Exception {
148         final int fakeHotspotSubAddr = 0x2b05;
149         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
150         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
151         LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
152                 mHotspotIpServer);
153         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
154         assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
155         when(mHotspotIpServer.getAddress()).thenReturn(address);
156 
157         address = mPrivateAddressCoordinator.requestDownstreamAddress(
158                 mUsbIpServer);
159         final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
160         assertNotEquals(predefinedPrefix, usbPrefix);
161 
162         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
163         mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
164         address = mPrivateAddressCoordinator.requestDownstreamAddress(
165                 mUsbIpServer);
166         final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address);
167         assertEquals("Fail to reselect available prefix: ", predefinedPrefix, allowUseFreePrefix);
168     }
169 
buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6, boolean isMobile)170     private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6,
171             boolean isMobile) {
172         final String testIface;
173         final String testIpv4Address;
174         if (isMobile) {
175             testIface = TEST_MOBILE_IFNAME;
176             testIpv4Address = "10.0.0.1";
177         } else {
178             testIface = TEST_WIFI_IFNAME;
179             testIpv4Address = "192.168.43.5";
180         }
181 
182         final LinkProperties prop = new LinkProperties();
183         prop.setInterfaceName(testIface);
184 
185         if (withIPv4) {
186             prop.addLinkAddress(
187                     new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address),
188                             NetworkConstants.IPV4_ADDR_BITS));
189         }
190 
191         if (withIPv6) {
192             prop.addLinkAddress(
193                     new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
194                             NetworkConstants.RFC7421_PREFIX_LENGTH));
195         }
196         return prop;
197     }
198 
199     @Test
testNoConflictUpstreamPrefix()200     public void testNoConflictUpstreamPrefix() throws Exception {
201         final int fakeHotspotSubId = 43;
202         final int fakeHotspotSubAddr = 0x2b05;
203         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
204         // Force always get subAddress "43.5" for conflict testing.
205         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
206         // 1. Enable hotspot with prefix 192.168.43.0/24
207         final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
208                 mHotspotIpServer);
209         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
210         assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
211         when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
212         // 2. Update v6 only mobile network, hotspot prefix should not be removed.
213         List<String> testConflicts;
214         final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true);
215         mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp);
216         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
217         mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork);
218         // 3. Update v4 only mobile network, hotspot prefix should not be removed.
219         final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true);
220         mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp);
221         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
222         // 4. Update v4v6 mobile network, hotspot prefix should not be removed.
223         final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true);
224         mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp);
225         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
226         // 5. Update v6 only wifi network, hotspot prefix should not be removed.
227         final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false);
228         mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp);
229         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
230         mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
231         // 6. Update v4 only wifi network, it conflict with hotspot prefix.
232         final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false);
233         mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
234         verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
235         reset(mHotspotIpServer);
236         // 7. Restart hotspot again and its prefix is different previous.
237         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
238         final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
239                 mHotspotIpServer);
240         final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2);
241         assertNotEquals(hotspotPrefix, hotspotPrefix2);
242         when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
243         mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
244         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
245         // 7. Usb tethering can be enabled and its prefix is different with conflict one.
246         final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
247                 mUsbIpServer);
248         final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr);
249         assertNotEquals(predefinedPrefix, usbPrefix);
250         assertNotEquals(hotspotPrefix2, usbPrefix);
251         when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
252         // 8. Disable wifi upstream, then wifi's prefix can be selected again.
253         mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
254         final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
255                 mEthernetIpServer);
256         final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
257         assertEquals(predefinedPrefix, ethPrefix);
258     }
259 }
260