From bbd16c57a07f70fe15a9d55d25a21c53e1a1194e Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Tue, 7 Jun 2022 16:36:33 -0700 Subject: [PATCH] TestNetworkService: Add support for toggling carrier on tun/tap Adds support for setting carrier to on/off for a tun/tap interface. It turns out that TUNSETCARRIER is also usable by tap interfaces, and TUNSETLINK does not work as expected. Test: atest EthernetManagerTest Change-Id: I39d70e0a65a34e7a4c4df70c53e4cc781a24a213 --- .../src/android/net/ITestNetworkManager.aidl | 2 ++ .../src/android/net/TestNetworkManager.java | 19 ++++++++++++ .../com_android_server_TestNetworkService.cpp | 29 ++++++++++++++----- .../android/server/TestNetworkService.java | 24 +++++++++++++-- 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/framework/src/android/net/ITestNetworkManager.aidl b/framework/src/android/net/ITestNetworkManager.aidl index 27d13c1687..b5aadc61eb 100644 --- a/framework/src/android/net/ITestNetworkManager.aidl +++ b/framework/src/android/net/ITestNetworkManager.aidl @@ -32,6 +32,8 @@ interface ITestNetworkManager TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs, in @nullable String iface); + void setCarrierEnabled(in TestNetworkInterface iface, boolean enabled); + void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered, in int[] administratorUids, in IBinder binder); diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java index 4e78823efc..8c6bf92be2 100644 --- a/framework/src/android/net/TestNetworkManager.java +++ b/framework/src/android/net/TestNetworkManager.java @@ -231,4 +231,23 @@ public class TestNetworkManager { throw e.rethrowFromSystemServer(); } } + + /** + * Enable / disable carrier on TestNetworkInterface + * + * Note: TUNSETCARRIER is not supported until kernel version 5.0. + * TODO: add RequiresApi annotation. + * + * @param iface the interface to configure. + * @param enabled true to turn carrier on, false to turn carrier off. + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS) + public void setCarrierEnabled(@NonNull TestNetworkInterface iface, boolean enabled) { + try { + mService.setCarrierEnabled(iface, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/service/jni/com_android_server_TestNetworkService.cpp b/service/jni/com_android_server_TestNetworkService.cpp index 4efd0e1a53..0a9a3a2b84 100644 --- a/service/jni/com_android_server_TestNetworkService.cpp +++ b/service/jni/com_android_server_TestNetworkService.cpp @@ -51,7 +51,15 @@ static void throwException(JNIEnv* env, int error, const char* action, const cha jniThrowException(env, "java/lang/IllegalStateException", msg.c_str()); } -static int createTunTapInterface(JNIEnv* env, bool isTun, const char* iface) { +// enable or disable carrier on tun / tap interface. +static void setTunTapCarrierEnabledImpl(JNIEnv* env, const char* iface, int tunFd, bool enabled) { + uint32_t carrierOn = enabled; + if (ioctl(tunFd, TUNSETCARRIER, &carrierOn)) { + throwException(env, errno, "set carrier", iface); + } +} + +static int createTunTapImpl(JNIEnv* env, bool isTun, const char* iface) { base::unique_fd tun(open("/dev/tun", O_RDWR | O_NONBLOCK)); ifreq ifr{}; @@ -79,23 +87,30 @@ static int createTunTapInterface(JNIEnv* env, bool isTun, const char* iface) { //------------------------------------------------------------------------------ -static jint create(JNIEnv* env, jobject /* thiz */, jboolean isTun, jstring jIface) { +static void setTunTapCarrierEnabled(JNIEnv* env, jclass /* clazz */, jstring + jIface, jint tunFd, jboolean enabled) { + ScopedUtfChars iface(env, jIface); + if (!iface.c_str()) { + jniThrowNullPointerException(env, "iface"); + } + setTunTapCarrierEnabledImpl(env, iface.c_str(), tunFd, enabled); +} + +static jint createTunTap(JNIEnv* env, jclass /* clazz */, jboolean isTun, jstring jIface) { ScopedUtfChars iface(env, jIface); if (!iface.c_str()) { jniThrowNullPointerException(env, "iface"); return -1; } - int tun = createTunTapInterface(env, isTun, iface.c_str()); - - // Any exceptions will be thrown from the createTunTapInterface call - return tun; + return createTunTapImpl(env, isTun, iface.c_str()); } //------------------------------------------------------------------------------ static const JNINativeMethod gMethods[] = { - {"jniCreateTunTap", "(ZLjava/lang/String;)I", (void*)create}, + {"nativeSetTunTapCarrierEnabled", "(Ljava/lang/String;IZ)V", (void*)setTunTapCarrierEnabled}, + {"nativeCreateTunTap", "(ZLjava/lang/String;)I", (void*)createTunTap}, }; int register_com_android_server_TestNetworkService(JNIEnv* env) { diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java index e12190ca2d..0d50b32c6f 100644 --- a/service/src/com/android/server/TestNetworkService.java +++ b/service/src/com/android/server/TestNetworkService.java @@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.net.module.util.NetdUtils; import com.android.net.module.util.NetworkStackConstants; +import java.io.IOException; import java.io.UncheckedIOException; import java.net.Inet4Address; import java.net.Inet6Address; @@ -76,7 +77,10 @@ class TestNetworkService extends ITestNetworkManager.Stub { @NonNull private final NetworkProvider mNetworkProvider; // Native method stubs - private static native int jniCreateTunTap(boolean isTun, @NonNull String iface); + private static native int nativeCreateTunTap(boolean isTun, @NonNull String iface); + + private static native void nativeSetTunTapCarrierEnabled(@NonNull String iface, int tunFd, + boolean enabled); @VisibleForTesting protected TestNetworkService(@NonNull Context context) { @@ -131,7 +135,7 @@ class TestNetworkService extends ITestNetworkManager.Stub { final long token = Binder.clearCallingIdentity(); try { ParcelFileDescriptor tunIntf = - ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, interfaceName)); + ParcelFileDescriptor.adoptFd(nativeCreateTunTap(isTun, interfaceName)); for (LinkAddress addr : linkAddrs) { mNetd.interfaceAddAddress( interfaceName, @@ -375,4 +379,20 @@ class TestNetworkService extends ITestNetworkManager.Stub { public static void enforceTestNetworkPermissions(@NonNull Context context) { context.enforceCallingOrSelfPermission(PERMISSION_NAME, "TestNetworkService"); } + + /** Enable / disable TestNetworkInterface carrier */ + @Override + public void setCarrierEnabled(@NonNull TestNetworkInterface iface, boolean enabled) { + enforceTestNetworkPermissions(mContext); + nativeSetTunTapCarrierEnabled(iface.getInterfaceName(), iface.getFileDescriptor().getFd(), + enabled); + // Explicitly close fd after use to prevent StrictMode from complaining. + // Also, explicitly referencing iface guarantees that the object is not garbage collected + // before nativeSetTunTapCarrierEnabled() executes. + try { + iface.getFileDescriptor().close(); + } catch (IOException e) { + // if the close fails, there is not much that can be done -- move on. + } + } }