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
This commit is contained in:
Patrick Rohr
2022-06-07 16:36:33 -07:00
parent 2ebff8a777
commit bbd16c57a0
4 changed files with 65 additions and 9 deletions

View File

@@ -32,6 +32,8 @@ interface ITestNetworkManager
TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs, TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs,
in @nullable String iface); in @nullable String iface);
void setCarrierEnabled(in TestNetworkInterface iface, boolean enabled);
void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered, void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
in int[] administratorUids, in IBinder binder); in int[] administratorUids, in IBinder binder);

View File

@@ -231,4 +231,23 @@ public class TestNetworkManager {
throw e.rethrowFromSystemServer(); 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();
}
}
} }

View File

@@ -51,7 +51,15 @@ static void throwException(JNIEnv* env, int error, const char* action, const cha
jniThrowException(env, "java/lang/IllegalStateException", msg.c_str()); 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)); base::unique_fd tun(open("/dev/tun", O_RDWR | O_NONBLOCK));
ifreq ifr{}; 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); ScopedUtfChars iface(env, jIface);
if (!iface.c_str()) { if (!iface.c_str()) {
jniThrowNullPointerException(env, "iface"); jniThrowNullPointerException(env, "iface");
return -1; return -1;
} }
int tun = createTunTapInterface(env, isTun, iface.c_str()); return createTunTapImpl(env, isTun, iface.c_str());
// Any exceptions will be thrown from the createTunTapInterface call
return tun;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = { 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) { int register_com_android_server_TestNetworkService(JNIEnv* env) {

View File

@@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetdUtils; import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetworkStackConstants; import com.android.net.module.util.NetworkStackConstants;
import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.Inet6Address; import java.net.Inet6Address;
@@ -76,7 +77,10 @@ class TestNetworkService extends ITestNetworkManager.Stub {
@NonNull private final NetworkProvider mNetworkProvider; @NonNull private final NetworkProvider mNetworkProvider;
// Native method stubs // 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 @VisibleForTesting
protected TestNetworkService(@NonNull Context context) { protected TestNetworkService(@NonNull Context context) {
@@ -131,7 +135,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
ParcelFileDescriptor tunIntf = ParcelFileDescriptor tunIntf =
ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, interfaceName)); ParcelFileDescriptor.adoptFd(nativeCreateTunTap(isTun, interfaceName));
for (LinkAddress addr : linkAddrs) { for (LinkAddress addr : linkAddrs) {
mNetd.interfaceAddAddress( mNetd.interfaceAddAddress(
interfaceName, interfaceName,
@@ -375,4 +379,20 @@ class TestNetworkService extends ITestNetworkManager.Stub {
public static void enforceTestNetworkPermissions(@NonNull Context context) { public static void enforceTestNetworkPermissions(@NonNull Context context) {
context.enforceCallingOrSelfPermission(PERMISSION_NAME, "TestNetworkService"); 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.
}
}
} }