• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 com.android.server;
18 
19 import static android.net.TestNetworkManager.CLAT_INTERFACE_PREFIX;
20 import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
21 import static android.net.TestNetworkManager.TEST_TUN_PREFIX;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.net.ConnectivityManager;
27 import android.net.INetd;
28 import android.net.ITestNetworkManager;
29 import android.net.IpPrefix;
30 import android.net.LinkAddress;
31 import android.net.LinkProperties;
32 import android.net.NetworkAgent;
33 import android.net.NetworkAgentConfig;
34 import android.net.NetworkCapabilities;
35 import android.net.NetworkProvider;
36 import android.net.RouteInfo;
37 import android.net.TestNetworkInterface;
38 import android.net.TestNetworkSpecifier;
39 import android.os.Binder;
40 import android.os.Handler;
41 import android.os.HandlerThread;
42 import android.os.IBinder;
43 import android.os.Looper;
44 import android.os.ParcelFileDescriptor;
45 import android.os.RemoteException;
46 import android.util.SparseArray;
47 
48 import com.android.internal.annotations.GuardedBy;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.net.module.util.NetdUtils;
51 import com.android.net.module.util.NetworkStackConstants;
52 
53 import java.io.UncheckedIOException;
54 import java.net.Inet4Address;
55 import java.net.Inet6Address;
56 import java.net.InterfaceAddress;
57 import java.net.NetworkInterface;
58 import java.net.SocketException;
59 import java.util.ArrayList;
60 import java.util.Objects;
61 import java.util.concurrent.atomic.AtomicInteger;
62 
63 /** @hide */
64 class TestNetworkService extends ITestNetworkManager.Stub {
65     @NonNull private static final String TEST_NETWORK_LOGTAG = "TestNetworkAgent";
66     @NonNull private static final String TEST_NETWORK_PROVIDER_NAME = "TestNetworkProvider";
67     @NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
68 
69     @NonNull private final Context mContext;
70     @NonNull private final INetd mNetd;
71 
72     @NonNull private final HandlerThread mHandlerThread;
73     @NonNull private final Handler mHandler;
74 
75     @NonNull private final ConnectivityManager mCm;
76     @NonNull private final NetworkProvider mNetworkProvider;
77 
78     // Native method stubs
jniCreateTunTap(boolean isTun, @NonNull String iface)79     private static native int jniCreateTunTap(boolean isTun, @NonNull String iface);
80 
81     @VisibleForTesting
TestNetworkService(@onNull Context context)82     protected TestNetworkService(@NonNull Context context) {
83         mHandlerThread = new HandlerThread("TestNetworkServiceThread");
84         mHandlerThread.start();
85         mHandler = new Handler(mHandlerThread.getLooper());
86 
87         mContext = Objects.requireNonNull(context, "missing Context");
88         mNetd = Objects.requireNonNull(
89                 INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
90                 "could not get netd instance");
91         mCm = mContext.getSystemService(ConnectivityManager.class);
92         mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
93                 TEST_NETWORK_PROVIDER_NAME);
94         final long token = Binder.clearCallingIdentity();
95         try {
96             mCm.registerNetworkProvider(mNetworkProvider);
97         } finally {
98             Binder.restoreCallingIdentity(token);
99         }
100     }
101 
102     // TODO: find a way to allow the caller to pass in non-clat interface names, ensuring that
103     // those names do not conflict with names created by callers that do not pass in an interface
104     // name.
isValidInterfaceName(@onNull final String iface)105     private static boolean isValidInterfaceName(@NonNull final String iface) {
106         return iface.startsWith(CLAT_INTERFACE_PREFIX + TEST_TUN_PREFIX)
107                 || iface.startsWith(CLAT_INTERFACE_PREFIX + TEST_TAP_PREFIX);
108     }
109 
110     /**
111      * Create a TUN or TAP interface with the specified parameters.
112      *
113      * <p>This method will return the FileDescriptor to the interface. Close it to tear down the
114      * interface.
115      */
116     @Override
createInterface(boolean isTun, boolean bringUp, LinkAddress[] linkAddrs, @Nullable String iface)117     public TestNetworkInterface createInterface(boolean isTun, boolean bringUp,
118             LinkAddress[] linkAddrs, @Nullable String iface) {
119         enforceTestNetworkPermissions(mContext);
120 
121         Objects.requireNonNull(linkAddrs, "missing linkAddrs");
122 
123         String interfaceName = iface;
124         if (iface == null) {
125             String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
126             interfaceName = ifacePrefix + sTestTunIndex.getAndIncrement();
127         } else if (!isValidInterfaceName(iface)) {
128             throw new IllegalArgumentException("invalid interface name requested: " + iface);
129         }
130 
131         final long token = Binder.clearCallingIdentity();
132         try {
133             ParcelFileDescriptor tunIntf =
134                     ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, interfaceName));
135             for (LinkAddress addr : linkAddrs) {
136                 mNetd.interfaceAddAddress(
137                         interfaceName,
138                         addr.getAddress().getHostAddress(),
139                         addr.getPrefixLength());
140             }
141 
142             if (bringUp) {
143                 NetdUtils.setInterfaceUp(mNetd, interfaceName);
144             }
145 
146             return new TestNetworkInterface(tunIntf, interfaceName);
147         } catch (RemoteException e) {
148             throw e.rethrowFromSystemServer();
149         } finally {
150             Binder.restoreCallingIdentity(token);
151         }
152     }
153 
154     // Tracker for TestNetworkAgents
155     @GuardedBy("mTestNetworkTracker")
156     @NonNull
157     private final SparseArray<TestNetworkAgent> mTestNetworkTracker = new SparseArray<>();
158 
159     public class TestNetworkAgent extends NetworkAgent implements IBinder.DeathRecipient {
160         private static final int NETWORK_SCORE = 1; // Use a low, non-zero score.
161 
162         private final int mUid;
163 
164         @GuardedBy("mBinderLock")
165         @NonNull
166         private IBinder mBinder;
167 
168         @NonNull private final Object mBinderLock = new Object();
169 
TestNetworkAgent( @onNull Context context, @NonNull Looper looper, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, @NonNull NetworkAgentConfig config, int uid, @NonNull IBinder binder, @NonNull NetworkProvider np)170         private TestNetworkAgent(
171                 @NonNull Context context,
172                 @NonNull Looper looper,
173                 @NonNull NetworkCapabilities nc,
174                 @NonNull LinkProperties lp,
175                 @NonNull NetworkAgentConfig config,
176                 int uid,
177                 @NonNull IBinder binder,
178                 @NonNull NetworkProvider np)
179                 throws RemoteException {
180             super(context, looper, TEST_NETWORK_LOGTAG, nc, lp, NETWORK_SCORE, config, np);
181             mUid = uid;
182             synchronized (mBinderLock) {
183                 mBinder = binder; // Binder null-checks in create()
184 
185                 try {
186                     mBinder.linkToDeath(this, 0);
187                 } catch (RemoteException e) {
188                     binderDied();
189                     throw e; // Abort, signal failure up the stack.
190                 }
191             }
192         }
193 
194         /**
195          * If the Binder object dies, this function is called to free the resources of this
196          * TestNetworkAgent
197          */
198         @Override
binderDied()199         public void binderDied() {
200             teardown();
201         }
202 
203         @Override
unwanted()204         protected void unwanted() {
205             teardown();
206         }
207 
teardown()208         private void teardown() {
209             unregister();
210 
211             // Synchronize on mBinderLock to ensure that unlinkToDeath is never called more than
212             // once (otherwise it could throw an exception)
213             synchronized (mBinderLock) {
214                 // If mBinder is null, this Test Network has already been cleaned up.
215                 if (mBinder == null) return;
216                 mBinder.unlinkToDeath(this, 0);
217                 mBinder = null;
218             }
219 
220             // Has to be in TestNetworkAgent to ensure all teardown codepaths properly clean up
221             // resources, even for binder death or unwanted calls.
222             synchronized (mTestNetworkTracker) {
223                 mTestNetworkTracker.remove(getNetwork().getNetId());
224             }
225         }
226     }
227 
registerTestNetworkAgent( @onNull Looper looper, @NonNull Context context, @NonNull String iface, @Nullable LinkProperties lp, boolean isMetered, int callingUid, @NonNull int[] administratorUids, @NonNull IBinder binder)228     private TestNetworkAgent registerTestNetworkAgent(
229             @NonNull Looper looper,
230             @NonNull Context context,
231             @NonNull String iface,
232             @Nullable LinkProperties lp,
233             boolean isMetered,
234             int callingUid,
235             @NonNull int[] administratorUids,
236             @NonNull IBinder binder)
237             throws RemoteException, SocketException {
238         Objects.requireNonNull(looper, "missing Looper");
239         Objects.requireNonNull(context, "missing Context");
240         // iface and binder validity checked by caller
241 
242         // Build narrow set of NetworkCapabilities, useful only for testing
243         NetworkCapabilities nc = new NetworkCapabilities();
244         nc.clearAll(); // Remove default capabilities.
245         nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
246         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
247         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
248         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
249         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
250         nc.setNetworkSpecifier(new TestNetworkSpecifier(iface));
251         nc.setAdministratorUids(administratorUids);
252         if (!isMetered) {
253             nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
254         }
255 
256         // Build LinkProperties
257         if (lp == null) {
258             lp = new LinkProperties();
259         } else {
260             lp = new LinkProperties(lp);
261             // Use LinkAddress(es) from the interface itself to minimize how much the caller
262             // is trusted.
263             lp.setLinkAddresses(new ArrayList<>());
264         }
265         lp.setInterfaceName(iface);
266 
267         // Find the currently assigned addresses, and add them to LinkProperties
268         boolean allowIPv4 = false, allowIPv6 = false;
269         NetworkInterface netIntf = NetworkInterface.getByName(iface);
270         Objects.requireNonNull(netIntf, "No such network interface found: " + netIntf);
271 
272         for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
273             lp.addLinkAddress(
274                     new LinkAddress(intfAddr.getAddress(), intfAddr.getNetworkPrefixLength()));
275 
276             if (intfAddr.getAddress() instanceof Inet6Address) {
277                 allowIPv6 |= !intfAddr.getAddress().isLinkLocalAddress();
278             } else if (intfAddr.getAddress() instanceof Inet4Address) {
279                 allowIPv4 = true;
280             }
281         }
282 
283         // Add global routes (but as non-default, non-internet providing network)
284         if (allowIPv4) {
285             lp.addRoute(new RouteInfo(new IpPrefix(
286                     NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface));
287         }
288         if (allowIPv6) {
289             lp.addRoute(new RouteInfo(new IpPrefix(
290                     NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
291         }
292 
293         final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
294                 new NetworkAgentConfig.Builder().build(), callingUid, binder,
295                 mNetworkProvider);
296         agent.register();
297         agent.markConnected();
298         return agent;
299     }
300 
301     /**
302      * Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS
303      * permission.
304      *
305      * <p>This method provides a Network that is useful only for testing.
306      */
307     @Override
setupTestNetwork( @onNull String iface, @Nullable LinkProperties lp, boolean isMetered, @NonNull int[] administratorUids, @NonNull IBinder binder)308     public void setupTestNetwork(
309             @NonNull String iface,
310             @Nullable LinkProperties lp,
311             boolean isMetered,
312             @NonNull int[] administratorUids,
313             @NonNull IBinder binder) {
314         enforceTestNetworkPermissions(mContext);
315 
316         Objects.requireNonNull(iface, "missing Iface");
317         Objects.requireNonNull(binder, "missing IBinder");
318 
319         if (!(iface.startsWith(INetd.IPSEC_INTERFACE_PREFIX)
320                 || iface.startsWith(TEST_TUN_PREFIX))) {
321             throw new IllegalArgumentException(
322                     "Cannot create network for non ipsec, non-testtun interface");
323         }
324 
325         try {
326             // Synchronize all accesses to mTestNetworkTracker to prevent the case where:
327             // 1. TestNetworkAgent successfully binds to death of binder
328             // 2. Before it is added to the mTestNetworkTracker, binder dies, binderDied() is called
329             // (on a different thread)
330             // 3. This thread is pre-empted, put() is called after remove()
331             synchronized (mTestNetworkTracker) {
332                 TestNetworkAgent agent =
333                         registerTestNetworkAgent(
334                                 mHandler.getLooper(),
335                                 mContext,
336                                 iface,
337                                 lp,
338                                 isMetered,
339                                 Binder.getCallingUid(),
340                                 administratorUids,
341                                 binder);
342 
343                 mTestNetworkTracker.put(agent.getNetwork().getNetId(), agent);
344             }
345         } catch (SocketException e) {
346             throw new UncheckedIOException(e);
347         } catch (RemoteException e) {
348             throw e.rethrowFromSystemServer();
349         }
350     }
351 
352     /** Teardown a test network */
353     @Override
teardownTestNetwork(int netId)354     public void teardownTestNetwork(int netId) {
355         enforceTestNetworkPermissions(mContext);
356 
357         final TestNetworkAgent agent;
358         synchronized (mTestNetworkTracker) {
359             agent = mTestNetworkTracker.get(netId);
360         }
361 
362         if (agent == null) {
363             return; // Already torn down
364         } else if (agent.mUid != Binder.getCallingUid()) {
365             throw new SecurityException("Attempted to modify other user's test networks");
366         }
367 
368         // Safe to be called multiple times.
369         agent.teardown();
370     }
371 
372     private static final String PERMISSION_NAME =
373             android.Manifest.permission.MANAGE_TEST_NETWORKS;
374 
enforceTestNetworkPermissions(@onNull Context context)375     public static void enforceTestNetworkPermissions(@NonNull Context context) {
376         context.enforceCallingOrSelfPermission(PERMISSION_NAME, "TestNetworkService");
377     }
378 }
379