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