1 /*
2  * Copyright (C) 2021 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 
17 package android.net.vcn.cts;
18 
19 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
20 
21 import android.annotation.NonNull;
22 import android.content.Context;
23 import android.ipsec.ike.cts.IkeTunUtils;
24 import android.net.ConnectivityManager;
25 import android.net.IpPrefix;
26 import android.net.LinkAddress;
27 import android.net.LinkProperties;
28 import android.net.Network;
29 import android.net.NetworkAgent;
30 import android.net.NetworkAgentConfig;
31 import android.net.NetworkCapabilities;
32 import android.net.NetworkProvider;
33 import android.net.NetworkRequest;
34 import android.net.RouteInfo;
35 import android.net.TestNetworkInterface;
36 import android.net.TestNetworkManager;
37 import android.net.TestNetworkSpecifier;
38 import android.net.vcn.VcnManager;
39 import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener;
40 import android.net.vcn.VcnNetworkPolicyResult;
41 import android.os.Looper;
42 import android.os.ParcelFileDescriptor;
43 import android.util.CloseGuard;
44 import android.util.Log;
45 
46 import com.android.net.module.util.NetworkStackConstants;
47 
48 import java.net.Inet4Address;
49 import java.net.Inet6Address;
50 import java.net.InetAddress;
51 import java.net.InterfaceAddress;
52 import java.net.NetworkInterface;
53 import java.util.Arrays;
54 import java.util.Objects;
55 import java.util.Set;
56 import java.util.concurrent.BlockingQueue;
57 import java.util.concurrent.Executor;
58 import java.util.concurrent.LinkedBlockingQueue;
59 import java.util.concurrent.TimeUnit;
60 
61 /** Utility class for cleanly creating and tearing down Test Networks. */
62 // TODO(b/188462344): compine with IKEv2's TestNetworkContext
63 public class TestNetworkWrapper implements AutoCloseable {
64     private static final String TAG = TestNetworkWrapper.class.getSimpleName();
65     private static final String NETWORK_AGENT_TAG = TestNetworkAgent.class.getSimpleName();
66     private static final String POLICY_LISTENER_TAG =
67             TestNetworkAgent.TestVcnNetworkPolicyChangeListener.class.getSimpleName();
68 
69     public static final int NETWORK_CB_TIMEOUT_MS = 5000;
70 
71     private static final int IP4_PREFIX_LEN = 32;
72     private static final int IP6_PREFIX_LEN = 64;
73 
74     // This NetworkRequest is expected to only match with Test Networks. To do so, remove all
75     // default Capabilities and specify TRANSPORT_TEST.
76     private static final NetworkRequest TEST_NETWORK_REQUEST =
77             new NetworkRequest.Builder()
78                     .clearCapabilities()
79                     .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
80                     .build();
81 
82     private static final String NETWORK_PROVIDER_NAME = "TestNetworkProvider";
83     private static final int TEST_NETWORK_SCORE = 1; // Use a low, non-zero score.
84     private static final Executor INLINE_EXECUTOR = Runnable::run;
85 
86     private final CloseGuard mCloseGuard = new CloseGuard();
87 
88     private final ConnectivityManager mConnectivityManager;
89     private final VcnManager mVcnManager;
90     private final TestNetworkManager mTestNetworkManager;
91 
92     private final TestNetworkAgent mTestNetworkAgent;
93 
94     public final VcnTestNetworkCallback vcnNetworkCallback;
95     public final ParcelFileDescriptor tunFd;
96     public final IkeTunUtils ikeTunUtils;
97     public final Network tunNetwork;
98 
TestNetworkWrapper( @onNull Context context, int mtu, boolean isMetered, @NonNull Set<Integer> subIds, @NonNull InetAddress localAddress)99     public TestNetworkWrapper(
100             @NonNull Context context,
101             int mtu,
102             boolean isMetered,
103             @NonNull Set<Integer> subIds,
104             @NonNull InetAddress localAddress)
105             throws Exception {
106         mConnectivityManager = context.getSystemService(ConnectivityManager.class);
107         mVcnManager = context.getSystemService(VcnManager.class);
108         mTestNetworkManager = context.getSystemService(TestNetworkManager.class);
109 
110         try {
111             final LinkAddress linkAddress =
112                     new LinkAddress(
113                             localAddress,
114                             localAddress instanceof Inet4Address ? IP4_PREFIX_LEN : IP6_PREFIX_LEN);
115             final TestNetworkInterface tni =
116                     mTestNetworkManager.createTunInterface(Arrays.asList(linkAddress));
117             tunFd = tni.getFileDescriptor();
118             final String iface = tni.getInterfaceName();
119 
120             final NetworkRequest nr =
121                     new NetworkRequest.Builder(TEST_NETWORK_REQUEST)
122                             .setNetworkSpecifier(iface)
123                             .build();
124             vcnNetworkCallback = new VcnTestNetworkCallback();
125             mConnectivityManager.requestNetwork(nr, vcnNetworkCallback);
126 
127             final NetworkCapabilities nc =
128                     createNetworkCapabilitiesForIface(iface, isMetered, subIds);
129             final LinkProperties lp = createLinkPropertiesForIface(iface, mtu);
130 
131             final VcnNetworkPolicyResult policy = mVcnManager.applyVcnNetworkPolicy(nc, lp);
132             if (policy.isTeardownRequested()) {
133                 throw new IllegalStateException("Restart requested in bringup");
134             }
135 
136             mTestNetworkAgent =
137                     new TestNetworkAgent(
138                             context, Looper.getMainLooper(), policy.getNetworkCapabilities(), lp);
139             mTestNetworkAgent.register();
140             mTestNetworkAgent.markConnected();
141 
142             tunNetwork = vcnNetworkCallback.waitForAvailable();
143             ikeTunUtils = new IkeTunUtils(tunFd);
144             mCloseGuard.open(TAG);
145         } catch (Exception e) {
146             Log.e(TAG, "Failed to bring up TestNetworkWrapper", e);
147             close();
148             throw e;
149         }
150     }
151 
createNetworkCapabilitiesForIface( @onNull String iface, boolean isMetered, Set<Integer> subIds)152     private static NetworkCapabilities createNetworkCapabilitiesForIface(
153             @NonNull String iface, boolean isMetered, Set<Integer> subIds) {
154         NetworkCapabilities.Builder builder =
155                 NetworkCapabilities.Builder.withoutDefaultCapabilities()
156                         .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
157                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
158                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
159                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
160                         .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
161                         .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
162                         .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)
163                         .setNetworkSpecifier(new TestNetworkSpecifier(iface))
164                         .setSubscriptionIds(subIds);
165         if (!isMetered) {
166             builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
167         }
168         return builder.build();
169     }
170 
createLinkPropertiesForIface(@onNull String iface, int mtu)171     private static LinkProperties createLinkPropertiesForIface(@NonNull String iface, int mtu)
172             throws Exception {
173         final LinkProperties lp = new LinkProperties();
174         lp.setInterfaceName(iface);
175         lp.setMtu(mtu);
176 
177         // Find the currently assigned addresses, and add them to LinkProperties
178         boolean allowIPv4 = false;
179         boolean allowIPv6 = false;
180         NetworkInterface netIntf = NetworkInterface.getByName(iface);
181         Objects.requireNonNull(netIntf, "No such network interface found: " + netIntf);
182 
183         for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
184             lp.addLinkAddress(
185                     new LinkAddress(intfAddr.getAddress(), intfAddr.getNetworkPrefixLength()));
186 
187             if (intfAddr.getAddress() instanceof Inet6Address) {
188                 allowIPv6 |= !intfAddr.getAddress().isLinkLocalAddress();
189             } else if (intfAddr.getAddress() instanceof Inet4Address) {
190                 allowIPv4 = true;
191             }
192         }
193 
194         // Add global routes (but as non-default, non-internet providing network). Use prefix
195         // lengths of 0 to match all IP addresses.
196         if (allowIPv4) {
197             lp.addRoute(
198                     new RouteInfo(
199                             new IpPrefix(NetworkStackConstants.IPV4_ADDR_ANY, 0 /* prefixLength */),
200                             null /* gateway */,
201                             iface,
202                             RouteInfo.RTN_UNICAST));
203         }
204         if (allowIPv6) {
205             lp.addRoute(
206                     new RouteInfo(
207                             new IpPrefix(NetworkStackConstants.IPV6_ADDR_ANY, 0 /* prefixLength */),
208                             null /* gateway */,
209                             iface,
210                             RouteInfo.RTN_UNICAST));
211         }
212 
213         return lp;
214     }
215 
216     @Override
close()217     public void close() {
218         mCloseGuard.close();
219 
220         if (vcnNetworkCallback != null) {
221             try {
222                 mConnectivityManager.unregisterNetworkCallback(vcnNetworkCallback);
223             } catch (Exception e) {
224                 Log.e(TAG, "Failed to unregister Network CB", e);
225             }
226         }
227 
228         if (mTestNetworkAgent != null) {
229             synchronized (mTestNetworkAgent) {
230                 try {
231                     mTestNetworkAgent.teardown();
232                 } catch (Exception e) {
233                     Log.e(TAG, "Failed to unregister TestNetworkAgent", e);
234                 }
235             }
236         }
237 
238         if (tunNetwork != null) {
239             try {
240                 mTestNetworkManager.teardownTestNetwork(tunNetwork);
241             } catch (Exception e) {
242                 Log.e(TAG, "Failed to tear down Test Network", e);
243             }
244         }
245 
246         if (tunFd != null) {
247             try {
248                 tunFd.close();
249             } catch (Exception e) {
250                 Log.e(TAG, "Failed to close Test Network FD", e);
251             }
252         }
253     }
254 
255     @Override
finalize()256     public void finalize() {
257         mCloseGuard.warnIfOpen();
258         close();
259     }
260 
261     /**
262      * Test-only NetworkAgent to be used for instrumented TUN Networks.
263      *
264      * <p>TestNetworkAgent is NOT THREAD SAFE - all accesses should be synchronized.
265      */
266     private class TestNetworkAgent extends NetworkAgent {
267         private final CloseGuard mCloseGuard = new CloseGuard();
268         private final VcnNetworkPolicyChangeListener mPolicyListener =
269                 new TestVcnNetworkPolicyChangeListener();
270 
271         private final LinkProperties mLinkProperties;
272 
273         private NetworkCapabilities mNetworkCapabilities;
274 
TestNetworkAgent( @onNull Context context, @NonNull Looper looper, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp)275         private TestNetworkAgent(
276                 @NonNull Context context,
277                 @NonNull Looper looper,
278                 @NonNull NetworkCapabilities nc,
279                 @NonNull LinkProperties lp) {
280             super(
281                     context,
282                     looper,
283                     NETWORK_AGENT_TAG,
284                     nc,
285                     lp,
286                     TEST_NETWORK_SCORE,
287                     new NetworkAgentConfig.Builder().build(),
288                     new NetworkProvider(context, looper, NETWORK_PROVIDER_NAME));
289 
290             mNetworkCapabilities = nc;
291             mLinkProperties = lp;
292 
293             mVcnManager.addVcnNetworkPolicyChangeListener(INLINE_EXECUTOR, mPolicyListener);
294 
295             mCloseGuard.open(NETWORK_AGENT_TAG);
296         }
297 
298         @Override
finalize()299         public void finalize() {
300             mCloseGuard.warnIfOpen();
301             teardown();
302         }
303 
304         @Override
onNetworkUnwanted()305         public void onNetworkUnwanted() {
306             // Not guaranteed to be called from the same thread, so synchronize on this.
307             synchronized (this) {
308                 teardown();
309             }
310         }
311 
teardown()312         private void teardown() {
313             mCloseGuard.close();
314             unregister();
315             mVcnManager.removeVcnNetworkPolicyChangeListener(mPolicyListener);
316         }
317 
getNetworkCapabilities()318         private NetworkCapabilities getNetworkCapabilities() {
319             return mNetworkCapabilities;
320         }
321 
updateNetworkCapabilities(@onNull NetworkCapabilities nc)322         private void updateNetworkCapabilities(@NonNull NetworkCapabilities nc) {
323             Objects.requireNonNull(nc, "nc must be non-null");
324 
325             mNetworkCapabilities = nc;
326             sendNetworkCapabilities(mNetworkCapabilities);
327         }
328 
getLinkProperties()329         private LinkProperties getLinkProperties() {
330             return mLinkProperties;
331         }
332 
333         private class TestVcnNetworkPolicyChangeListener implements VcnNetworkPolicyChangeListener {
334             @Override
onPolicyChanged()335             public void onPolicyChanged() {
336                 synchronized (TestNetworkAgent.this) {
337                     final VcnNetworkPolicyResult policy =
338                             mVcnManager.applyVcnNetworkPolicy(
339                                     mTestNetworkAgent.getNetworkCapabilities(),
340                                     mTestNetworkAgent.getLinkProperties());
341                     if (policy.isTeardownRequested()) {
342                         Log.w(POLICY_LISTENER_TAG, "network teardown requested on policy change");
343                         teardown();
344                         return;
345                     }
346 
347                     updateNetworkCapabilities(policy.getNetworkCapabilities());
348                 }
349             }
350         }
351     }
352 
353     /** NetworkCallback to used for tracking test network events. */
354     // TODO(b/187231331): remove once TestNetworkCallback supports tracking NetworkCapabilities
355     public static class VcnTestNetworkCallback extends TestNetworkCallback {
356         private final BlockingQueue<Network> mAvailableHistory = new LinkedBlockingQueue<>();
357         private final BlockingQueue<Network> mLostHistory = new LinkedBlockingQueue<>();
358         private final BlockingQueue<CapabilitiesChangedEvent> mCapabilitiesChangedHistory =
359                 new LinkedBlockingQueue<>();
360 
361         @Override
waitForAvailable()362         public Network waitForAvailable() throws InterruptedException {
363             return mAvailableHistory.poll(NETWORK_CB_TIMEOUT_MS, TimeUnit.MILLISECONDS);
364         }
365 
366         @Override
waitForLost()367         public Network waitForLost() throws InterruptedException {
368             return mLostHistory.poll(NETWORK_CB_TIMEOUT_MS, TimeUnit.MILLISECONDS);
369         }
370 
waitForOnCapabilitiesChanged()371         public CapabilitiesChangedEvent waitForOnCapabilitiesChanged() throws Exception {
372             return waitForOnCapabilitiesChanged(NETWORK_CB_TIMEOUT_MS);
373         }
374 
waitForOnCapabilitiesChanged(long timeoutMillis)375         public CapabilitiesChangedEvent waitForOnCapabilitiesChanged(long timeoutMillis)
376                 throws Exception {
377             return mCapabilitiesChangedHistory.poll(timeoutMillis, TimeUnit.MILLISECONDS);
378         }
379 
380         @Override
onAvailable(@onNull Network network)381         public void onAvailable(@NonNull Network network) {
382             mAvailableHistory.offer(network);
383         }
384 
385         @Override
onLost(@onNull Network network)386         public void onLost(@NonNull Network network) {
387             mLostHistory.offer(network);
388         }
389 
390         @Override
onCapabilitiesChanged( @onNull Network network, @NonNull NetworkCapabilities nc)391         public void onCapabilitiesChanged(
392                 @NonNull Network network, @NonNull NetworkCapabilities nc) {
393             mCapabilitiesChangedHistory.offer(new CapabilitiesChangedEvent(network, nc));
394         }
395 
396         public class CapabilitiesChangedEvent {
397             public final Network network;
398             public final NetworkCapabilities networkCapabilities;
399 
CapabilitiesChangedEvent( @onNull Network network, @NonNull NetworkCapabilities networkCapabilities)400             public CapabilitiesChangedEvent(
401                     @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
402                 this.network = network;
403                 this.networkCapabilities = networkCapabilities;
404             }
405         }
406     }
407 }
408