Merge changes Ie66ba631,Ic42c09a3

am: 6525e97ea5

Change-Id: I5c8a3364f0244c5945be2bbe919aac2dcc4eba66
This commit is contained in:
Benedict Wong
2019-03-08 06:35:04 -08:00
committed by android-build-merger
4 changed files with 610 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
/**
* This class is used to return the interface name and fd of the test interface
*
* @hide
*/
@TestApi
public final class TestNetworkInterface implements Parcelable {
private static final String TAG = "TestNetworkInterface";
private final ParcelFileDescriptor mFileDescriptor;
private final String mInterfaceName;
@Override
public int describeContents() {
return (mFileDescriptor != null) ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(mFileDescriptor, PARCELABLE_WRITE_RETURN_VALUE);
out.writeString(mInterfaceName);
}
public TestNetworkInterface(ParcelFileDescriptor pfd, String intf) {
mFileDescriptor = pfd;
mInterfaceName = intf;
}
private TestNetworkInterface(Parcel in) {
mFileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
mInterfaceName = in.readString();
}
public ParcelFileDescriptor getFileDescriptor() {
return mFileDescriptor;
}
public String getInterfaceName() {
return mInterfaceName;
}
public static final Parcelable.Creator<TestNetworkInterface> CREATOR =
new Parcelable.Creator<TestNetworkInterface>() {
public TestNetworkInterface createFromParcel(Parcel in) {
return new TestNetworkInterface(in);
}
public TestNetworkInterface[] newArray(int size) {
return new TestNetworkInterface[size];
}
};
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
/**
* Class that allows creation and management of per-app, test-only networks
*
* @hide
*/
@TestApi
public class TestNetworkManager {
@NonNull private static final String TAG = TestNetworkManager.class.getSimpleName();
@NonNull private final ITestNetworkManager mService;
@NonNull private final Context mContext;
/** @hide */
public TestNetworkManager(@NonNull Context context, @NonNull ITestNetworkManager service) {
mContext = Preconditions.checkNotNull(context, "missing Context");
mService = Preconditions.checkNotNull(service, "missing ITestNetworkManager");
}
/**
* Teardown the capability-limited, testing-only network for a given interface
*
* @param network The test network that should be torn down
* @hide
*/
@TestApi
public void teardownTestNetwork(@NonNull Network network) {
try {
mService.teardownTestNetwork(network.netId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets up a capability-limited, testing-only network for a given interface
*
* @param iface the name of the interface to be used for the Network LinkProperties.
* @param binder A binder object guarding the lifecycle of this test network.
* @hide
*/
@TestApi
public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
try {
mService.setupTestNetwork(iface, binder);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Create a tun interface for testing purposes
*
* @param linkAddrs an array of LinkAddresses to assign to the TUN interface
* @return A ParcelFileDescriptor of the underlying TUN interface. Close this to tear down the
* TUN interface.
* @hide
*/
@TestApi
public TestNetworkInterface createTunInterface(@NonNull LinkAddress[] linkAddrs) {
try {
return mService.createTunInterface(linkAddrs);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -0,0 +1,338 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.ITestNetworkManager;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.TestNetworkInterface;
import android.net.util.NetdService;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.concurrent.atomic.AtomicInteger;
/** @hide */
class TestNetworkService extends ITestNetworkManager.Stub {
@NonNull private static final String TAG = TestNetworkService.class.getSimpleName();
@NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK";
@NonNull private static final String TEST_TUN_PREFIX = "testtun";
@NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
@NonNull private final Context mContext;
@NonNull private final INetworkManagementService mNMS;
@NonNull private final INetd mNetd;
@NonNull private final HandlerThread mHandlerThread;
@NonNull private final Handler mHandler;
// Native method stubs
private static native int jniCreateTun(@NonNull String iface);
@VisibleForTesting
protected TestNetworkService(
@NonNull Context context, @NonNull INetworkManagementService netManager) {
mHandlerThread = new HandlerThread("TestNetworkServiceThread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mContext = checkNotNull(context, "missing Context");
mNMS = checkNotNull(netManager, "missing INetworkManagementService");
mNetd = checkNotNull(NetdService.getInstance(), "could not get netd instance");
}
/**
* Create a TUN interface with the given interface name and link addresses
*
* <p>This method will return the FileDescriptor to the TUN interface. Close it to tear down the
* TUN interface.
*/
@Override
public TestNetworkInterface createTunInterface(@NonNull LinkAddress[] linkAddrs) {
enforceTestNetworkPermissions(mContext);
checkNotNull(linkAddrs, "missing linkAddrs");
String iface = TEST_TUN_PREFIX + sTestTunIndex.getAndIncrement();
return Binder.withCleanCallingIdentity(
() -> {
try {
ParcelFileDescriptor tunIntf =
ParcelFileDescriptor.adoptFd(jniCreateTun(iface));
for (LinkAddress addr : linkAddrs) {
mNetd.interfaceAddAddress(
iface,
addr.getAddress().getHostAddress(),
addr.getPrefixLength());
}
return new TestNetworkInterface(tunIntf, iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
});
}
// Tracker for TestNetworkAgents
@GuardedBy("mTestNetworkTracker")
@NonNull
private final SparseArray<TestNetworkAgent> mTestNetworkTracker = new SparseArray<>();
public class TestNetworkAgent extends NetworkAgent implements IBinder.DeathRecipient {
private static final int NETWORK_SCORE = 1; // Use a low, non-zero score.
private final int mUid;
@NonNull private final NetworkInfo mNi;
@NonNull private final NetworkCapabilities mNc;
@NonNull private final LinkProperties mLp;
@GuardedBy("mBinderLock")
@NonNull
private IBinder mBinder;
@NonNull private final Object mBinderLock = new Object();
private TestNetworkAgent(
@NonNull Looper looper,
@NonNull Context context,
@NonNull NetworkInfo ni,
@NonNull NetworkCapabilities nc,
@NonNull LinkProperties lp,
int uid,
@NonNull IBinder binder)
throws RemoteException {
super(looper, context, TEST_NETWORK_TYPE, ni, nc, lp, NETWORK_SCORE);
mUid = uid;
mNi = ni;
mNc = nc;
mLp = lp;
synchronized (mBinderLock) {
mBinder = binder; // Binder null-checks in create()
try {
mBinder.linkToDeath(this, 0);
} catch (RemoteException e) {
binderDied();
throw e; // Abort, signal failure up the stack.
}
}
}
/**
* If the Binder object dies, this function is called to free the resources of this
* TestNetworkAgent
*/
@Override
public void binderDied() {
teardown();
}
@Override
protected void unwanted() {
teardown();
}
private void teardown() {
mNi.setDetailedState(DetailedState.DISCONNECTED, null, null);
mNi.setIsAvailable(false);
sendNetworkInfo(mNi);
// Synchronize on mBinderLock to ensure that unlinkToDeath is never called more than
// once (otherwise it could throw an exception)
synchronized (mBinderLock) {
// If mBinder is null, this Test Network has already been cleaned up.
if (mBinder == null) return;
mBinder.unlinkToDeath(this, 0);
mBinder = null;
}
// Has to be in TestNetworkAgent to ensure all teardown codepaths properly clean up
// resources, even for binder death or unwanted calls.
synchronized (mTestNetworkTracker) {
mTestNetworkTracker.remove(netId);
}
}
}
private TestNetworkAgent registerTestNetworkAgent(
@NonNull Looper looper,
@NonNull Context context,
@NonNull String iface,
int callingUid,
@NonNull IBinder binder)
throws RemoteException, SocketException {
checkNotNull(looper, "missing Looper");
checkNotNull(context, "missing Context");
// iface and binder validity checked by caller
// Build network info with special testing type
NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_TEST, 0, TEST_NETWORK_TYPE, "");
ni.setDetailedState(DetailedState.CONNECTED, null, null);
ni.setIsAvailable(true);
// Build narrow set of NetworkCapabilities, useful only for testing
NetworkCapabilities nc = new NetworkCapabilities();
nc.clearAll(); // Remove default capabilities.
nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
// Build LinkProperties
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(iface);
// Find the currently assigned addresses, and add them to LinkProperties
boolean allowIPv4 = false, allowIPv6 = false;
NetworkInterface netIntf = NetworkInterface.getByName(iface);
checkNotNull(netIntf, "No such network interface found: " + netIntf);
for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
lp.addLinkAddress(
new LinkAddress(intfAddr.getAddress(), intfAddr.getNetworkPrefixLength()));
if (intfAddr.getAddress() instanceof Inet6Address) {
allowIPv6 |= !intfAddr.getAddress().isLinkLocalAddress();
} else if (intfAddr.getAddress() instanceof Inet4Address) {
allowIPv4 = true;
}
}
// Add global routes (but as non-default, non-internet providing network)
if (allowIPv4) {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface));
}
if (allowIPv6) {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
}
return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
}
/**
* Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS
* permission.
*
* <p>This method provides a Network that is useful only for testing.
*/
@Override
public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
enforceTestNetworkPermissions(mContext);
checkNotNull(iface, "missing Iface");
checkNotNull(binder, "missing IBinder");
if (!(iface.startsWith(INetd.IPSEC_INTERFACE_PREFIX)
|| iface.startsWith(TEST_TUN_PREFIX))) {
throw new IllegalArgumentException(
"Cannot create network for non ipsec, non-testtun interface");
}
// Setup needs to be done with NETWORK_STACK privileges.
int callingUid = Binder.getCallingUid();
Binder.withCleanCallingIdentity(
() -> {
try {
mNMS.setInterfaceUp(iface);
// Synchronize all accesses to mTestNetworkTracker to prevent the case
// where:
// 1. TestNetworkAgent successfully binds to death of binder
// 2. Before it is added to the mTestNetworkTracker, binder dies,
// binderDied() is called (on a different thread)
// 3. This thread is pre-empted, put() is called after remove()
synchronized (mTestNetworkTracker) {
TestNetworkAgent agent =
registerTestNetworkAgent(
mHandler.getLooper(),
mContext,
iface,
callingUid,
binder);
mTestNetworkTracker.put(agent.netId, agent);
}
} catch (SocketException e) {
throw new UncheckedIOException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
});
}
/** Teardown a test network */
@Override
public void teardownTestNetwork(int netId) {
enforceTestNetworkPermissions(mContext);
TestNetworkAgent agent;
synchronized (mTestNetworkTracker) {
agent = mTestNetworkTracker.get(netId);
}
if (agent == null) {
return; // Already torn down
} else if (agent.mUid != Binder.getCallingUid()) {
throw new SecurityException("Attempted to modify other user's test networks");
}
// Safe to be called multiple times.
agent.teardown();
}
// STOPSHIP: Change this back to android.Manifest.permission.MANAGE_TEST_NETWORKS
private static final String PERMISSION_NAME = "dummy";
public static void enforceTestNetworkPermissions(@NonNull Context context) {
// STOPSHIP: Re-enable these checks. Disabled until adoptShellPermissionIdentity() can be
// called from CTS test code.
if (false) {
context.enforceCallingOrSelfPermission(PERMISSION_NAME, "TestNetworkService");
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_NDEBUG 0
#define LOG_TAG "TestNetworkServiceJni"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/ipv6_route.h>
#include <linux/route.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <log/log.h>
#include "netutils/ifc.h"
#include "jni.h"
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
namespace android {
//------------------------------------------------------------------------------
static void throwException(JNIEnv* env, int error, const char* action, const char* iface) {
const std::string& msg =
android::base::StringPrintf("Error %s %s: %s", action, iface, strerror(error));
jniThrowException(env, "java/lang/IllegalStateException", msg.c_str());
}
static int createTunInterface(JNIEnv* env, const char* iface) {
base::unique_fd tun(open("/dev/tun", O_RDWR | O_NONBLOCK));
ifreq ifr{};
// Allocate interface.
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
if (ioctl(tun.get(), TUNSETIFF, &ifr)) {
throwException(env, errno, "allocating", ifr.ifr_name);
return -1;
}
// Activate interface using an unconnected datagram socket.
base::unique_fd inet6CtrlSock(socket(AF_INET6, SOCK_DGRAM, 0));
ifr.ifr_flags = IFF_UP;
if (ioctl(inet6CtrlSock.get(), SIOCSIFFLAGS, &ifr)) {
throwException(env, errno, "activating", ifr.ifr_name);
return -1;
}
return tun.release();
}
//------------------------------------------------------------------------------
static jint create(JNIEnv* env, jobject /* thiz */, jstring jIface) {
ScopedUtfChars iface(env, jIface);
if (!iface.c_str()) {
jniThrowNullPointerException(env, "iface");
return -1;
}
int tun = createTunInterface(env, iface.c_str());
// Any exceptions will be thrown from the createTunInterface call
return tun;
}
//------------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
{"jniCreateTun", "(Ljava/lang/String;)I", (void*)create},
};
int register_android_server_TestNetworkService(JNIEnv* env) {
return jniRegisterNativeMethods(env, "com/android/server/TestNetworkService", gMethods,
NELEM(gMethods));
}
}; // namespace android