ClatCoordinator: use Java class BpfMap to add clat rule
This a preparation to replace duplicated native functions with the
existing Java class.
Bug: 221213090
Test: atest FrameworksNetTests
Test: check map content {iface index, v4addr, v6addr, pfx96} manually.
The map fileds are the same as "ip addr" and clatd logging.
$ adb shell dumpsys netd
ClatdController
BPF ingress map: iif(iface) nat64Prefix v6Addr -> v4Addr oif(iface)
47(wlan0) 64:ff9b::/96 2a00:79e1:abc:6f02:f9e1:8c2d:604e:bc06 -> 192.0.0.4 53(v4-wlan0)
BPF egress map: iif(iface) v4Addr -> v6Addr nat64Prefix oif(iface)
53(v4-wlan0) 192.0.0.4 -> 2a00:79e1:abc:6f02:f9e1:8c2d:604e:bc06 64:ff9b::/96 47(wlan0) ether
$ adb shell ip addr
47: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ..
53: v4-wlan0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 ..
03-14 18:35:04.822 30852 30852 I clatd : Starting clat version 1.5 on wlan0 plat=64:ff9b:: v4=192.0.0.4 v6=2a00:79e1:abc:6f02:f9e1:8c2d:604e:bc06
Change-Id: I91d0cb6e76c4ef7bacf91b996786308ff4918f35
diff --git a/service/native/libs/libclat/bpfhelper.cpp b/service/native/libs/libclat/bpfhelper.cpp
index 00785ad..c05a058 100644
--- a/service/native/libs/libclat/bpfhelper.cpp
+++ b/service/native/libs/libclat/bpfhelper.cpp
@@ -82,43 +82,6 @@
}
unique_fd rxProgFd(rv);
- ClatEgress4Key txKey = {
- .iif = tracker.v4ifIndex,
- .local4 = tracker.v4,
- };
- ClatEgress4Value txValue = {
- .oif = tracker.ifIndex,
- .local6 = tracker.v6,
- .pfx96 = tracker.pfx96,
- .oifIsEthernet = isEthernet.value(),
- };
-
- auto ret = mClatEgress4Map.writeValue(txKey, txValue, BPF_ANY);
- if (!ret.ok()) {
- ALOGE("mClatEgress4Map.writeValue failure: %s", strerror(ret.error().code()));
- return;
- }
-
- ClatIngress6Key rxKey = {
- .iif = tracker.ifIndex,
- .pfx96 = tracker.pfx96,
- .local6 = tracker.v6,
- };
- ClatIngress6Value rxValue = {
- // TODO: move all the clat code to eBPF and remove the tun interface entirely.
- .oif = tracker.v4ifIndex,
- .local4 = tracker.v4,
- };
-
- ret = mClatIngress6Map.writeValue(rxKey, rxValue, BPF_ANY);
- if (!ret.ok()) {
- ALOGE("mClatIngress6Map.writeValue failure: %s", strerror(ret.error().code()));
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- return;
- }
-
// We do tc setup *after* populating the maps, so scanning through them
// can always be used to tell us what needs cleanup.
@@ -130,12 +93,6 @@
if (rv) {
ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface,
strerror(-rv));
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- ret = mClatIngress6Map.deleteValue(rxKey);
- if (!ret.ok())
- ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
return;
}
@@ -148,12 +105,6 @@
// with interface addition, the lifetime is till interface deletion. Moreover, the clsact
// has no clat filter now. It should not break anything.
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- ret = mClatIngress6Map.deleteValue(rxKey);
- if (!ret.ok())
- ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
return;
}
@@ -170,12 +121,6 @@
// The v4- interface clsact is not deleted. See the reason in the error unwinding code of
// the egress filter attaching of v4- tun interface.
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- ret = mClatIngress6Map.deleteValue(rxKey);
- if (!ret.ok())
- ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
return;
}
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 2e26ae4..253e56c 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -30,10 +30,19 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.system.ErrnoException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BpfMap;
+import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.InterfaceParams;
+import com.android.net.module.util.TcUtils;
+import com.android.net.module.util.bpf.ClatEgress4Key;
+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 java.io.FileDescriptor;
import java.io.IOException;
@@ -71,11 +80,22 @@
private static final int INVALID_IFINDEX = 0;
+ private static final String CLAT_EGRESS4_MAP_PATH = makeMapPath("egress4");
+ private static final String CLAT_INGRESS6_MAP_PATH = makeMapPath("ingress6");
+
+ private static String makeMapPath(String which) {
+ return "/sys/fs/bpf/map_clatd_clat_" + which + "_map";
+ }
+
@NonNull
private final INetd mNetd;
@NonNull
private final Dependencies mDeps;
@Nullable
+ private final IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap;
+ @Nullable
+ private final IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap;
+ @Nullable
private ClatdTracker mClatdTracker = null;
@VisibleForTesting
@@ -195,6 +215,45 @@
public void untagSocket(long cookie) throws IOException {
native_untagSocket(cookie);
}
+
+ /** Get ingress6 BPF map. */
+ @Nullable
+ public IBpfMap<ClatIngress6Key, ClatIngress6Value> getBpfIngress6Map() {
+ // 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<>(CLAT_INGRESS6_MAP_PATH,
+ BpfMap.BPF_F_RDWR, ClatIngress6Key.class, ClatIngress6Value.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create ingress6 map: " + e);
+ return null;
+ }
+ }
+
+ /** Get egress4 BPF map. */
+ @Nullable
+ public IBpfMap<ClatEgress4Key, ClatEgress4Value> getBpfEgress4Map() {
+ // 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<>(CLAT_EGRESS4_MAP_PATH,
+ BpfMap.BPF_F_RDWR, ClatEgress4Key.class, ClatEgress4Value.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create egress4 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);
+ }
}
@VisibleForTesting
@@ -268,6 +327,50 @@
public ClatCoordinator(@NonNull Dependencies deps) {
mDeps = deps;
mNetd = mDeps.getNetd();
+ mIngressMap = mDeps.getBpfIngress6Map();
+ mEgressMap = mDeps.getBpfEgress4Map();
+ }
+
+ private void maybeStartBpf(final ClatdTracker tracker) {
+ if (mIngressMap == null || mEgressMap == null) return;
+
+ final boolean isEthernet;
+ try {
+ isEthernet = mDeps.isEthernet(tracker.iface);
+ } catch (IOException e) {
+ Log.e(TAG, "Fail to call isEthernet for interface " + tracker.iface);
+ return;
+ }
+
+ final ClatEgress4Key txKey = new ClatEgress4Key(tracker.v4ifIndex, tracker.v4);
+ final ClatEgress4Value txValue = new ClatEgress4Value(tracker.ifIndex, tracker.v6,
+ tracker.pfx96, (short) (isEthernet ? 1 /* ETHER */ : 0 /* RAWIP */));
+ try {
+ mEgressMap.insertEntry(txKey, txValue);
+ } catch (ErrnoException | IllegalStateException e) {
+ Log.e(TAG, "Could not insert entry (" + txKey + ", " + txValue + ") on egress map: "
+ + e);
+ return;
+ }
+
+ final ClatIngress6Key rxKey = new ClatIngress6Key(tracker.ifIndex, tracker.pfx96,
+ tracker.v6);
+ final ClatIngress6Value rxValue = new ClatIngress6Value(tracker.v4ifIndex,
+ tracker.v4);
+ try {
+ mIngressMap.insertEntry(rxKey, rxValue);
+ } catch (ErrnoException | IllegalStateException e) {
+ Log.e(TAG, "Could not insert entry (" + rxKey + ", " + rxValue + ") ingress map: "
+ + e);
+ try {
+ mEgressMap.deleteEntry(txKey);
+ } catch (ErrnoException | IllegalStateException e2) {
+ Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e2);
+ }
+ return;
+ }
+
+ // TODO: attach program.
}
/**
@@ -454,6 +557,9 @@
mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96,
pid, cookie);
+ // [7] Start BPF
+ maybeStartBpf(mClatdTracker);
+
return v6Str;
}
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index 6c8b545..dc81038 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -41,6 +41,11 @@
import androidx.test.filters.SmallTest;
+import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.bpf.ClatEgress4Key;
+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.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -98,8 +103,19 @@
private static final ParcelFileDescriptor PACKET_SOCK_PFD = new ParcelFileDescriptor(
new FileDescriptor());
+ private static final ClatEgress4Key EGRESS_KEY = new ClatEgress4Key(STACKED_IFINDEX,
+ INET4_LOCAL4);
+ private static final ClatEgress4Value EGRESS_VALUE = new ClatEgress4Value(BASE_IFINDEX,
+ INET6_LOCAL6, INET6_PFX96, (short) 1 /* oifIsEthernet, 1 = true */);
+ private static final ClatIngress6Key INGRESS_KEY = new ClatIngress6Key(BASE_IFINDEX,
+ INET6_PFX96, INET6_LOCAL6);
+ private static final ClatIngress6Value INGRESS_VALUE = new ClatIngress6Value(STACKED_IFINDEX,
+ INET4_LOCAL4);
+
@Mock private INetd mNetd;
@Spy private TestDependencies mDeps = new TestDependencies();
+ @Mock private IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap;
+ @Mock private IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap;
/**
* The dependency injection class is used to mock the JNI functions and system functions
@@ -298,6 +314,26 @@
fail("unsupported arg: " + cookie);
}
}
+
+ /** Get ingress6 BPF map. */
+ @Override
+ public IBpfMap<ClatIngress6Key, ClatIngress6Value> getBpfIngress6Map() {
+ return mIngressMap;
+ }
+
+ /** Get egress4 BPF map. */
+ @Override
+ public IBpfMap<ClatEgress4Key, ClatEgress4Value> getBpfEgress4Map() {
+ return mEgressMap;
+ }
+
+ /** Checks if the network interface uses an ethernet L2 header. */
+ public boolean isEthernet(String iface) throws IOException {
+ if (BASE_IFACE.equals(iface)) return true;
+
+ fail("unsupported arg: " + iface);
+ return false;
+ }
};
@NonNull
@@ -322,8 +358,8 @@
@Test
public void testStartStopClatd() throws Exception {
final ClatCoordinator coordinator = makeClatCoordinator();
- final InOrder inOrder = inOrder(mNetd, mDeps);
- clearInvocations(mNetd, mDeps);
+ final InOrder inOrder = inOrder(mNetd, mDeps, mIngressMap, mEgressMap);
+ clearInvocations(mNetd, mDeps, mIngressMap, mEgressMap);
// [1] Start clatd.
final String addr6For464xlat = coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX);
@@ -379,6 +415,8 @@
argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)),
eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING));
+ inOrder.verify(mEgressMap).insertEntry(eq(EGRESS_KEY), eq(EGRESS_VALUE));
+ inOrder.verify(mIngressMap).insertEntry(eq(INGRESS_KEY), eq(INGRESS_VALUE));
inOrder.verifyNoMoreInteractions();
// [2] Start clatd again failed.