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 
130     @Test
testReserveBluetoothPrefix()131     public void testReserveBluetoothPrefix() throws Exception {
132         final int fakeSubAddr = 0x2c05;
133         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
134         LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
135                 mHotspotIpServer);
136         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
137         assertNotEquals("Should not get reserved prefix: ", mBluetoothPrefix, hotspotPrefix);
138         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
139     }
140 
141     @Test
testNoConflictDownstreamPrefix()142     public void testNoConflictDownstreamPrefix() throws Exception {
143         final int fakeHotspotSubAddr = 0x2b05;
144         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
145         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
146         LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
147                 mHotspotIpServer);
148         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
149         assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
150         when(mHotspotIpServer.getAddress()).thenReturn(address);
151 
152         address = mPrivateAddressCoordinator.requestDownstreamAddress(
153                 mUsbIpServer);
154         final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
155         assertNotEquals(predefinedPrefix, usbPrefix);
156 
157         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
158         mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
159         address = mPrivateAddressCoordinator.requestDownstreamAddress(
160                 mUsbIpServer);
161         final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address);
162         assertEquals("Fail to reselect available perfix: ", predefinedPrefix, allowUseFreePrefix);
163     }
164 
buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6, boolean isMobile)165     private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6,
166             boolean isMobile) {
167         final String testIface;
168         final String testIpv4Address;
169         if (isMobile) {
170             testIface = TEST_MOBILE_IFNAME;
171             testIpv4Address = "10.0.0.1";
172         } else {
173             testIface = TEST_WIFI_IFNAME;
174             testIpv4Address = "192.168.43.5";
175         }
176 
177         final LinkProperties prop = new LinkProperties();
178         prop.setInterfaceName(testIface);
179 
180         if (withIPv4) {
181             prop.addLinkAddress(
182                     new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address),
183                             NetworkConstants.IPV4_ADDR_BITS));
184         }
185 
186         if (withIPv6) {
187             prop.addLinkAddress(
188                     new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
189                             NetworkConstants.RFC7421_PREFIX_LENGTH));
190         }
191         return prop;
192     }
193 
194     @Test
testNoConflictUpstreamPrefix()195     public void testNoConflictUpstreamPrefix() throws Exception {
196         final int fakeHotspotSubId = 43;
197         final int fakeHotspotSubAddr = 0x2b05;
198         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
199         // Force always get subAddress "43.5" for conflict testing.
200         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
201         // 1. Enable hotspot with prefix 192.168.43.0/24
202         final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
203                 mHotspotIpServer);
204         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
205         assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
206         when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
207         // 2. Update v6 only mobile network, hotspot prefix should not be removed.
208         List<String> testConflicts;
209         final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true);
210         mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp);
211         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
212         mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork);
213         // 3. Update v4 only mobile network, hotspot prefix should not be removed.
214         final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true);
215         mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp);
216         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
217         // 4. Update v4v6 mobile network, hotspot prefix should not be removed.
218         final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true);
219         mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp);
220         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
221         // 5. Update v6 only wifi network, hotspot prefix should not be removed.
222         final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false);
223         mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp);
224         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
225         mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
226         // 6. Update v4 only wifi network, it conflict with hotspot prefix.
227         final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false);
228         mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
229         verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
230         reset(mHotspotIpServer);
231         // 7. Restart hotspot again and its prefix is different previous.
232         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
233         final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
234                 mHotspotIpServer);
235         final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2);
236         assertNotEquals(hotspotPrefix, hotspotPrefix2);
237         when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
238         mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
239         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
240         // 7. Usb tethering can be enabled and its prefix is different with conflict one.
241         final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
242                 mUsbIpServer);
243         final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr);
244         assertNotEquals(predefinedPrefix, usbPrefix);
245         assertNotEquals(hotspotPrefix2, usbPrefix);
246         when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
247         // 8. Disable wifi upstream, then wifi's prefix can be selected again.
248         mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
249         final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
250                 mEthernetIpServer);
251         final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
252         assertEquals(predefinedPrefix, ethPrefix);
253     }
254 }
255