diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp index 71ddd1155d..4517b5cf5c 100644 --- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp +++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,11 @@ #include #include +#include +#include +#include #include +#include #include "libclat/bpfhelper.h" #include "libclat/clatutils.h" @@ -473,6 +478,72 @@ static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* en stopClatdProcess(pid); } +static jlong com_android_server_connectivity_ClatCoordinator_tagSocketAsClat( + JNIEnv* env, jobject clazz, jobject sockJavaFd) { + int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd); + if (sockFd < 0) { + jniThrowExceptionFmt(env, "java/io/IOException", "Invalid socket file descriptor"); + return -1; + } + + uint64_t sock_cookie = bpf::getSocketCookie(sockFd); + if (sock_cookie == bpf::NONEXISTENT_COOKIE) { + throwIOException(env, "get socket cookie failed", errno); + return -1; + } + + bpf::BpfMap cookieTagMap; + auto res = cookieTagMap.init(COOKIE_TAG_MAP_PATH); + if (!res.ok()) { + throwIOException(env, "failed to init the cookieTagMap", res.error().code()); + return -1; + } + + // Tag raw socket with uid AID_CLAT and set tag as zero because tag is unused in bpf + // program for counting data usage in netd.c. Tagging socket is used to avoid counting + // duplicated clat traffic in bpf stat. + UidTagValue newKey = {.uid = (uint32_t)AID_CLAT, .tag = 0 /* unused */}; + res = cookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY); + if (!res.ok()) { + jniThrowExceptionFmt(env, "java/io/IOException", "Failed to tag the socket: %s, fd: %d", + strerror(res.error().code()), cookieTagMap.getMap().get()); + return -1; + } + + ALOGI("tag uid AID_CLAT to socket fd %d, cookie %" PRIu64 "", sockFd, sock_cookie); + return static_cast(sock_cookie); +} + +static void com_android_server_connectivity_ClatCoordinator_untagSocket(JNIEnv* env, jobject clazz, + jlong cookie) { + uint64_t sock_cookie = static_cast(cookie); + if (sock_cookie == bpf::NONEXISTENT_COOKIE) { + jniThrowExceptionFmt(env, "java/io/IOException", "Invalid socket cookie"); + return; + } + + // The reason that deleting entry from cookie tag map directly is that the tag socket destroy + // listener only monitors on group INET_TCP, INET_UDP, INET6_TCP, INET6_UDP. The other socket + // types, ex: raw, are not able to be removed automatically by the listener. + // See TrafficController::makeSkDestroyListener. + bpf::BpfMap cookieTagMap; + auto res = cookieTagMap.init(COOKIE_TAG_MAP_PATH); + if (!res.ok()) { + throwIOException(env, "failed to init the cookieTagMap", res.error().code()); + return; + } + + res = cookieTagMap.deleteValue(sock_cookie); + if (!res.ok()) { + jniThrowExceptionFmt(env, "java/io/IOException", "Failed to untag the socket: %s", + strerror(res.error().code())); + return; + } + + ALOGI("untag socket cookie %" PRIu64 "", sock_cookie); + return; +} + /* * JNI registration. */ @@ -502,6 +573,10 @@ static const JNINativeMethod gMethods[] = { {"native_stopClatd", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V", (void*)com_android_server_connectivity_ClatCoordinator_stopClatd}, + {"native_tagSocketAsClat", "(Ljava/io/FileDescriptor;)J", + (void*)com_android_server_connectivity_ClatCoordinator_tagSocketAsClat}, + {"native_untagSocket", "(J)V", + (void*)com_android_server_connectivity_ClatCoordinator_untagSocket}, }; int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env) { diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java index c57983b055..c1a8195b28 100644 --- a/service/src/com/android/server/connectivity/ClatCoordinator.java +++ b/service/src/com/android/server/connectivity/ClatCoordinator.java @@ -67,6 +67,7 @@ public class ClatCoordinator { private static final int INVALID_IFINDEX = 0; private static final int INVALID_PID = 0; + private static final long INVALID_COOKIE = 0; @NonNull private final INetd mNetd; @@ -81,6 +82,7 @@ public class ClatCoordinator { @Nullable private String mXlatLocalAddress6 = null; private int mPid = INVALID_PID; + private long mCookie = INVALID_COOKIE; @VisibleForTesting abstract static class Dependencies { @@ -185,6 +187,20 @@ public class ClatCoordinator { throws IOException { native_stopClatd(iface, pfx96, v4, v6, pid); } + + /** + * Tag socket as clat. + */ + public long tagSocketAsClat(@NonNull FileDescriptor sock) throws IOException { + return native_tagSocketAsClat(sock); + } + + /** + * Untag socket. + */ + public void untagSocket(long cookie) throws IOException { + native_untagSocket(cookie); + } } @VisibleForTesting @@ -335,6 +351,17 @@ public class ClatCoordinator { throw new IOException("add anycast sockopt failed: " + e); } + // Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting. + long cookie; + try { + cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor()); + } catch (IOException e) { + tunFd.close(); + readSock6.close(); + writeSock6.close(); + throw new IOException("tag raw socket failed: " + e); + } + // Update our packet socket filter to reflect the new 464xlat IP address. try { mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6, ifaceIndex); @@ -353,7 +380,9 @@ public class ClatCoordinator { mNat64Prefix = pfx96; mXlatLocalAddress4 = v4; mXlatLocalAddress6 = v6; + mCookie = cookie; } catch (IOException e) { + mDeps.untagSocket(cookie); throw new IOException("Error start clatd on " + iface + ": " + e); } finally { tunFd.close(); @@ -374,7 +403,7 @@ public class ClatCoordinator { Log.i(TAG, "Stopping clatd pid=" + mPid + " on " + mIface); mDeps.stopClatd(mIface, mNat64Prefix, mXlatLocalAddress4, mXlatLocalAddress6, mPid); - // TODO: remove setIptablesDropRule + mDeps.untagSocket(mCookie); Log.i(TAG, "clatd on " + mIface + " stopped"); @@ -383,6 +412,7 @@ public class ClatCoordinator { mXlatLocalAddress4 = null; mXlatLocalAddress6 = null; mPid = INVALID_PID; + mCookie = INVALID_COOKIE; } private static native String native_selectIpv4Address(String v4addr, int prefixlen) @@ -403,4 +433,6 @@ public class ClatCoordinator { throws IOException; private static native void native_stopClatd(String iface, String pfx96, String v4, String v6, int pid) throws IOException; + private static native long native_tagSocketAsClat(FileDescriptor sock) throws IOException; + private static native void native_untagSocket(long cookie) throws IOException; } diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java index 84e02ce3f4..8a2cfc2da8 100644 --- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java +++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java @@ -26,13 +26,10 @@ import static com.android.testutils.MiscAsserts.assertThrows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; import android.annotation.NonNull; import android.net.INetd; @@ -82,6 +79,7 @@ public class ClatCoordinatorTest { private static final int TUN_FD = 534; private static final int RAW_SOCK_FD = 535; private static final int PACKET_SOCK_FD = 536; + private static final long RAW_SOCK_COOKIE = 27149; private static final ParcelFileDescriptor TUN_PFD = new ParcelFileDescriptor( new FileDescriptor()); private static final ParcelFileDescriptor RAW_SOCK_PFD = new ParcelFileDescriptor( @@ -258,12 +256,35 @@ public class ClatCoordinatorTest { /** * Stop clatd. */ + @Override public void stopClatd(@NonNull String iface, @NonNull String pfx96, @NonNull String v4, @NonNull String v6, int pid) throws IOException { if (pid == -1) { fail("unsupported arg: " + pid); } } + + /** + * Tag socket as clat. + */ + @Override + public long tagSocketAsClat(@NonNull FileDescriptor sock) throws IOException { + if (Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), sock)) { + return RAW_SOCK_COOKIE; + } + fail("unsupported arg: " + sock); + return 0; + } + + /** + * Untag socket. + */ + @Override + public void untagSocket(long cookie) throws IOException { + if (cookie != RAW_SOCK_COOKIE) { + fail("unsupported arg: " + cookie); + } + } }; @NonNull @@ -326,6 +347,8 @@ public class ClatCoordinatorTest { inOrder.verify(mDeps).addAnycastSetsockopt( argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(BASE_IFINDEX)); + inOrder.verify(mDeps).tagSocketAsClat( + argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd))); inOrder.verify(mDeps).configurePacketSocket( argThat(fd -> Objects.equals(PACKET_SOCK_PFD.getFileDescriptor(), fd)), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(BASE_IFINDEX)); @@ -348,13 +371,12 @@ public class ClatCoordinatorTest { coordinator.clatStop(); inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING), eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID)); + inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE)); inOrder.verifyNoMoreInteractions(); // [4] Expect an IO exception while stopping a clatd that doesn't exist. assertThrows("java.io.IOException: Clatd has not started", IOException.class, () -> coordinator.clatStop()); - inOrder.verify(mDeps, never()).stopClatd(anyString(), anyString(), anyString(), - anyString(), anyInt()); inOrder.verifyNoMoreInteractions(); }