diff --git a/service/Android.bp b/service/Android.bp index 76f9153e06..5ba6a0099e 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -57,12 +57,18 @@ cc_library_shared { ], srcs: [ "jni/com_android_server_TestNetworkService.cpp", + "jni/com_android_server_connectivity_ClatCoordinator.cpp", "jni/onload.cpp", ], stl: "libc++_static", header_libs: [ "libbase_headers", ], + static_libs: [ + "libclat", + "libip_checksum", + "libnetjniutils", + ], shared_libs: [ "liblog", "libnativehelper", diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp new file mode 100644 index 0000000000..e042bf3db9 --- /dev/null +++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 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. + */ + +#include +#include + +#include + +#include "libclat/clatutils.h" +#include "nativehelper/scoped_utf_chars.h" + +namespace android { +jstring com_android_server_connectivity_ClatCoordinator_selectIpv4Address(JNIEnv* env, + jobject clazz, + jstring v4addr, + jint prefixlen) { + ScopedUtfChars address(env, v4addr); + in_addr ip; + if (inet_pton(AF_INET, address.c_str(), &ip) != 1) { + return nullptr; + } + + // Pick an IPv4 address. + // TODO: this picks the address based on other addresses that are assigned to interfaces, but + // the address is only actually assigned to an interface once clatd starts up. So we could end + // up with two clatd instances with the same IPv4 address. + // Stop doing this and instead pick a free one from the kV4Addr pool. + in_addr v4 = {net::clat::selectIpv4Address(ip, prefixlen)}; + if (v4.s_addr == INADDR_NONE) { + jniThrowExceptionFmt(env, "java/io/IOException", "No free IPv4 address in %s/%d", + address.c_str(), prefixlen); + return nullptr; + } + + char addrstr[INET_ADDRSTRLEN]; + if (!inet_ntop(AF_INET, (void*)&v4, addrstr, sizeof(addrstr))) { + return nullptr; + } + return env->NewStringUTF(addrstr); +} + +/* + * JNI registration. + */ +static const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"selectIpv4Address", "(Ljava/lang/String;I)Ljava/lang/String;", + (void*)com_android_server_connectivity_ClatCoordinator_selectIpv4Address}, +}; + +int register_android_server_connectivity_ClatCoordinator(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/android/server/connectivity/ClatCoordinator", + gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/service/jni/onload.cpp b/service/jni/onload.cpp index 00128794bc..04d9671fad 100644 --- a/service/jni/onload.cpp +++ b/service/jni/onload.cpp @@ -20,6 +20,7 @@ namespace android { int register_android_server_TestNetworkService(JNIEnv* env); +int register_android_server_connectivity_ClatCoordinator(JNIEnv* env); extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { JNIEnv *env; @@ -32,6 +33,10 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { return JNI_ERR; } + if (register_android_server_connectivity_ClatCoordinator(env) < 0) { + return JNI_ERR; + } + return JNI_VERSION_1_6; } diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java new file mode 100644 index 0000000000..cf22e24284 --- /dev/null +++ b/service/src/com/android/server/connectivity/ClatCoordinator.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 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.connectivity; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.INetd; +import android.net.IpPrefix; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.IOException; + +/** + * This coordinator is responsible for providing clat relevant functionality. + * + * {@hide} + */ +public class ClatCoordinator { + private static final String TAG = ClatCoordinator.class.getSimpleName(); + + // For historical reasons, start with 192.0.0.4, and after that, use all subsequent addresses + // in 192.0.0.0/29 (RFC 7335). + @VisibleForTesting + static final String INIT_V4ADDR_STRING = "192.0.0.4"; + @VisibleForTesting + static final int INIT_V4ADDR_PREFIX_LEN = 29; + + private static final int INVALID_PID = 0; + + @NonNull + private final INetd mNetd; + @NonNull + private final Dependencies mDeps; + @Nullable + private String mIface = null; + private int mPid = INVALID_PID; + + @VisibleForTesting + abstract static class Dependencies { + /** + * Get netd. + */ + @NonNull + public abstract INetd getNetd(); + + /** + * Pick an IPv4 address for clat. + */ + @NonNull + public String jniSelectIpv4Address(@NonNull String v4addr, int prefixlen) + throws IOException { + return selectIpv4Address(v4addr, prefixlen); + } + } + + public ClatCoordinator(@NonNull Dependencies deps) { + mDeps = deps; + mNetd = mDeps.getNetd(); + } + + /** + * Start clatd for a given interface and NAT64 prefix. + */ + public String clatStart(final String iface, final int netId, + @NonNull final IpPrefix nat64Prefix) + throws IOException { + if (nat64Prefix.getPrefixLength() != 96) { + throw new IOException("Prefix must be 96 bits long: " + nat64Prefix); + } + + // [1] Pick an IPv4 address from 192.0.0.4, 192.0.0.5, 192.0.0.6 .. + final String v4; + try { + v4 = mDeps.jniSelectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN); + } catch (IOException e) { + throw new IOException("no IPv4 addresses were available for clat: " + e); + } + + // TODO: start clatd and returns local xlat464 v6 address. + return null; + } + + private static native String selectIpv4Address(String v4addr, int prefixlen) + throws IOException; +}