Merge changes Ic18521b6,Ibede8137
* changes:
Move tag/untag socket BPF map access from JNI to Java
Move struct CookieTagMap{Key, Value} to Connectivity/common
diff --git a/service-t/src/com/android/server/net/CookieTagMapKey.java b/common/src/com/android/net/module/util/bpf/CookieTagMapKey.java
similarity index 95%
rename from service-t/src/com/android/server/net/CookieTagMapKey.java
rename to common/src/com/android/net/module/util/bpf/CookieTagMapKey.java
index 443e5b3..17da7a0 100644
--- a/service-t/src/com/android/server/net/CookieTagMapKey.java
+++ b/common/src/com/android/net/module/util/bpf/CookieTagMapKey.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.net;
+package com.android.net.module.util.bpf;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
diff --git a/service-t/src/com/android/server/net/CookieTagMapValue.java b/common/src/com/android/net/module/util/bpf/CookieTagMapValue.java
similarity index 95%
rename from service-t/src/com/android/server/net/CookieTagMapValue.java
rename to common/src/com/android/net/module/util/bpf/CookieTagMapValue.java
index 93b9195..e1a221f 100644
--- a/service-t/src/com/android/server/net/CookieTagMapValue.java
+++ b/common/src/com/android/net/module/util/bpf/CookieTagMapValue.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.net;
+package com.android.net.module.util.bpf;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 8032303..77931b1 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -158,6 +158,8 @@
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.Struct.U32;
import com.android.net.module.util.Struct.U8;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
import java.io.File;
import java.io.FileDescriptor;
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index e2c5a63..de0e20a 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -421,7 +421,7 @@
stopClatdProcess(pid);
}
-static jlong com_android_server_connectivity_ClatCoordinator_tagSocketAsClat(
+static jlong com_android_server_connectivity_ClatCoordinator_getSocketCookie(
JNIEnv* env, jobject clazz, jobject sockJavaFd) {
int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd);
if (sockFd < 0) {
@@ -435,58 +435,10 @@
return -1;
}
- bpf::BpfMap<uint64_t, UidTagValue> 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);
+ ALOGI("Get cookie %" PRIu64 " for socket fd %d", sock_cookie, sockFd);
return static_cast<jlong>(sock_cookie);
}
-static void com_android_server_connectivity_ClatCoordinator_untagSocket(JNIEnv* env, jobject clazz,
- jlong cookie) {
- uint64_t sock_cookie = static_cast<uint64_t>(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<uint64_t, UidTagValue> 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.
*/
@@ -516,10 +468,8 @@
{"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},
+ {"native_getSocketCookie", "(Ljava/io/FileDescriptor;)J",
+ (void*)com_android_server_connectivity_ClatCoordinator_getSocketCookie},
};
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 5ea586a..6c4a021 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -46,6 +46,8 @@
import com.android.net.module.util.bpf.ClatEgress4Value;
import com.android.net.module.util.bpf.ClatIngress6Key;
import com.android.net.module.util.bpf.ClatIngress6Value;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -63,6 +65,10 @@
public class ClatCoordinator {
private static final String TAG = ClatCoordinator.class.getSimpleName();
+ // Sync from system/core/libcutils/include/private/android_filesystem_config.h
+ @VisibleForTesting
+ static final int AID_CLAT = 1029;
+
// Sync from external/android-clat/clatd.c
// 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header.
@VisibleForTesting
@@ -97,6 +103,8 @@
@VisibleForTesting
static final int PRIO_CLAT = 4;
+ private static final String COOKIE_TAG_MAP_PATH =
+ "/sys/fs/bpf/netd_shared/map_netd_cookie_tag_map";
private static final String CLAT_EGRESS4_MAP_PATH = makeMapPath("egress4");
private static final String CLAT_INGRESS6_MAP_PATH = makeMapPath("ingress6");
@@ -121,6 +129,8 @@
@Nullable
private final IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap;
@Nullable
+ private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap;
+ @Nullable
private ClatdTracker mClatdTracker = null;
/**
@@ -232,17 +242,10 @@
}
/**
- * Tag socket as clat.
+ * Get socket cookie.
*/
- public long tagSocketAsClat(@NonNull FileDescriptor sock) throws IOException {
- return native_tagSocketAsClat(sock);
- }
-
- /**
- * Untag socket.
- */
- public void untagSocket(long cookie) throws IOException {
- native_untagSocket(cookie);
+ public long getSocketCookie(@NonNull FileDescriptor sock) throws IOException {
+ return native_getSocketCookie(sock);
}
/** Get ingress6 BPF map. */
@@ -279,6 +282,23 @@
}
}
+ /** Get cookie tag map */
+ @Nullable
+ public IBpfMap<CookieTagMapKey, CookieTagMapValue> getBpfCookieTagMap() {
+ // Pre-T devices don't use ClatCoordinator to access clat map. Since Nat464Xlat
+ // initializes a ClatCoordinator object to avoid redundant null pointer check
+ // while using, ignore the BPF map initialization on pre-T devices.
+ // TODO: probably don't initialize ClatCoordinator object on pre-T devices.
+ if (!SdkLevel.isAtLeastT()) return null;
+ try {
+ return new BpfMap<>(COOKIE_TAG_MAP_PATH,
+ BpfMap.BPF_F_RDWR, CookieTagMapKey.class, CookieTagMapValue.class);
+ } catch (ErrnoException e) {
+ Log.wtf(TAG, "Cannot open cookie tag map: " + e);
+ return null;
+ }
+ }
+
/** Checks if the network interface uses an ethernet L2 header. */
public boolean isEthernet(String iface) throws IOException {
return TcUtils.isEthernet(iface);
@@ -388,6 +408,7 @@
mNetd = mDeps.getNetd();
mIngressMap = mDeps.getBpfIngress6Map();
mEgressMap = mDeps.getBpfEgress4Map();
+ mCookieTagMap = mDeps.getBpfCookieTagMap();
}
private void maybeStartBpf(final ClatdTracker tracker) {
@@ -536,6 +557,43 @@
}
}
+ private void tagSocketAsClat(long cookie) throws IOException {
+ if (mCookieTagMap == null) {
+ throw new IOException("Cookie tag map is not initialized");
+ }
+
+ // 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.
+ final CookieTagMapKey key = new CookieTagMapKey(cookie);
+ final CookieTagMapValue value = new CookieTagMapValue(AID_CLAT, 0 /* tag, unused */);
+ try {
+ mCookieTagMap.insertEntry(key, value);
+ } catch (ErrnoException | IllegalStateException e) {
+ throw new IOException("Could not insert entry (" + key + ", " + value
+ + ") on cookie tag map: " + e);
+ }
+ Log.i(TAG, "tag socket cookie " + cookie);
+ }
+
+ private void untagSocket(long cookie) throws IOException {
+ if (mCookieTagMap == null) {
+ throw new IOException("Cookie tag map is not initialized");
+ }
+
+ // 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.
+ final CookieTagMapKey key = new CookieTagMapKey(cookie);
+ try {
+ mCookieTagMap.deleteEntry(key);
+ } catch (ErrnoException | IllegalStateException e) {
+ throw new IOException("Could not delete entry (" + key + ") on cookie tag map: " + e);
+ }
+ Log.i(TAG, "untag socket cookie " + cookie);
+ }
+
/**
* Start clatd for a given interface and NAT64 prefix.
*/
@@ -686,7 +744,8 @@
// Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting.
final long cookie;
try {
- cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor());
+ cookie = mDeps.getSocketCookie(writeSock6.getFileDescriptor());
+ tagSocketAsClat(cookie);
} catch (IOException e) {
maybeCleanUp(tunFd, readSock6, writeSock6);
throw new IOException("tag raw socket failed: " + e);
@@ -696,6 +755,11 @@
try {
mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6Str, ifIndex);
} catch (IOException e) {
+ try {
+ untagSocket(cookie);
+ } catch (IOException e2) {
+ Log.e(TAG, "untagSocket cookie " + cookie + " failed: " + e2);
+ }
maybeCleanUp(tunFd, readSock6, writeSock6);
throw new IOException("configure packet socket failed: " + e);
}
@@ -706,8 +770,11 @@
pid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(),
writeSock6.getFileDescriptor(), iface, pfx96Str, v4Str, v6Str);
} catch (IOException e) {
- // TODO: probably refactor to handle the exception of #untagSocket if any.
- mDeps.untagSocket(cookie);
+ try {
+ untagSocket(cookie);
+ } catch (IOException e2) {
+ Log.e(TAG, "untagSocket cookie " + cookie + " failed: " + e2);
+ }
throw new IOException("Error start clatd on " + iface + ": " + e);
} finally {
// The file descriptors have been duplicated (dup2) to clatd in native_startClatd().
@@ -774,7 +841,7 @@
mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(),
mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(),
mClatdTracker.pid);
- mDeps.untagSocket(mClatdTracker.cookie);
+ untagSocket(mClatdTracker.cookie);
Log.i(TAG, "clatd on " + mClatdTracker.iface + " stopped");
mClatdTracker = null;
@@ -870,6 +937,5 @@
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;
+ private static native long native_getSocketCookie(FileDescriptor sock) 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 feee293..bbb61cd 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -21,6 +21,7 @@
import static android.system.OsConstants.ETH_P_IPV6;
import static com.android.net.module.util.NetworkStackConstants.ETHER_MTU;
+import static com.android.server.connectivity.ClatCoordinator.AID_CLAT;
import static com.android.server.connectivity.ClatCoordinator.CLAT_MAX_MTU;
import static com.android.server.connectivity.ClatCoordinator.EGRESS;
import static com.android.server.connectivity.ClatCoordinator.INGRESS;
@@ -56,6 +57,8 @@
import com.android.net.module.util.bpf.ClatEgress4Value;
import com.android.net.module.util.bpf.ClatIngress6Key;
import com.android.net.module.util.bpf.ClatIngress6Value;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.TestBpfMap;
@@ -127,11 +130,16 @@
INET6_PFX96, INET6_LOCAL6);
private static final ClatIngress6Value INGRESS_VALUE = new ClatIngress6Value(STACKED_IFINDEX,
INET4_LOCAL4);
+ private static final CookieTagMapKey COOKIE_TAG_KEY = new CookieTagMapKey(RAW_SOCK_COOKIE);
+ private static final CookieTagMapValue COOKIE_TAG_VALUE = new CookieTagMapValue(AID_CLAT,
+ 0 /* tag, unused */);
private final TestBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap =
spy(new TestBpfMap<>(ClatIngress6Key.class, ClatIngress6Value.class));
private final TestBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap =
spy(new TestBpfMap<>(ClatEgress4Key.class, ClatEgress4Value.class));
+ private final TestBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap =
+ spy(new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class));
@Mock private INetd mNetd;
@Spy private TestDependencies mDeps = new TestDependencies();
@@ -313,25 +321,10 @@
}
/**
- * Tag socket as clat.
+ * Get socket cookie.
*/
- @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);
- }
+ public long getSocketCookie(@NonNull FileDescriptor sock) throws IOException {
+ return RAW_SOCK_COOKIE;
}
/** Get ingress6 BPF map. */
@@ -346,6 +339,12 @@
return mEgressMap;
}
+ /** Get cookie tag map */
+ @Override
+ public IBpfMap<CookieTagMapKey, CookieTagMapValue> getBpfCookieTagMap() {
+ return mCookieTagMap;
+ }
+
/** Checks if the network interface uses an ethernet L2 header. */
public boolean isEthernet(String iface) throws IOException {
if (BASE_IFACE.equals(iface)) return true;
@@ -400,8 +399,8 @@
@Test
public void testStartStopClatd() throws Exception {
final ClatCoordinator coordinator = makeClatCoordinator();
- final InOrder inOrder = inOrder(mNetd, mDeps, mIngressMap, mEgressMap);
- clearInvocations(mNetd, mDeps, mIngressMap, mEgressMap);
+ final InOrder inOrder = inOrder(mNetd, mDeps, mIngressMap, mEgressMap, mCookieTagMap);
+ clearInvocations(mNetd, mDeps, mIngressMap, mEgressMap, mCookieTagMap);
// [1] Start clatd.
final String addr6For464xlat = coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX);
@@ -444,8 +443,9 @@
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(
+ inOrder.verify(mDeps).getSocketCookie(
argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)));
+ inOrder.verify(mCookieTagMap).insertEntry(eq(COOKIE_TAG_KEY), eq(COOKIE_TAG_VALUE));
inOrder.verify(mDeps).configurePacketSocket(
argThat(fd -> Objects.equals(PACKET_SOCK_PFD.getFileDescriptor(), fd)),
eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(BASE_IFINDEX));
@@ -481,7 +481,7 @@
inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
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.verify(mCookieTagMap).deleteEntry(eq(COOKIE_TAG_KEY));
assertNull(coordinator.getClatdTrackerForTesting());
inOrder.verifyNoMoreInteractions();
@@ -680,18 +680,6 @@
}
@Test
- public void testNotStartClatWithNativeFailureTagSocketAsClat() throws Exception {
- class FailureDependencies extends TestDependencies {
- @Override
- public long tagSocketAsClat(@NonNull FileDescriptor sock) throws IOException {
- throw new IOException();
- }
- }
- checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
- true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
- }
-
- @Test
public void testNotStartClatWithNativeFailureConfigurePacketSocket() throws Exception {
class FailureDependencies extends TestDependencies {
@Override
@@ -718,4 +706,28 @@
checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
}
+
+ @Test
+ public void testNotStartClatWithNativeFailureGetSocketCookie() throws Exception {
+ class FailureDependencies extends TestDependencies {
+ @Override
+ public long getSocketCookie(@NonNull FileDescriptor sock) throws IOException {
+ throw new IOException();
+ }
+ }
+ checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+ true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
+ }
+
+ @Test
+ public void testNotStartClatWithNullCookieTagMap() throws Exception {
+ class FailureDependencies extends TestDependencies {
+ @Override
+ public IBpfMap<CookieTagMapKey, CookieTagMapValue> getBpfCookieTagMap() {
+ return null;
+ }
+ }
+ checkNotStartClat(new FailureDependencies(), true /* needToCloseTunFd */,
+ true /* needToClosePacketSockFd */, true /* needToCloseRawSockFd */);
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 1a5620b..484d717 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -133,6 +133,8 @@
import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.Struct.U32;
import com.android.net.module.util.Struct.U8;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
import com.android.server.net.NetworkStatsService.AlertObserver;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;