ClatCoordinator: wrap clatd information

This is a preparation for accessing BPF program and map from existing
Java class. The new class ClatdTracker stores the clatd information
for forking clatd, accessing BPF maps and attaching BPF programs.
Using an object is easier to pass all required parameters and reuse
known resource.

Also refactor existing function and rename existing variables for this
commit.

Bug: 221213090
Test: atest FrameworksNetTests
Change-Id: I02ea889de89329c52710726fbcf1d2eac61707d7
This commit is contained in:
Hungming Chen
2022-03-16 11:57:59 +08:00
parent cc29690fc1
commit 328d15271b
2 changed files with 134 additions and 45 deletions

View File

@@ -24,6 +24,7 @@ import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.net.INetd; import android.net.INetd;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel; import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix; import android.net.IpPrefix;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
@@ -36,8 +37,11 @@ import com.android.net.module.util.InterfaceParams;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Objects;
/** /**
* This coordinator is responsible for providing clat relevant functionality. * This coordinator is responsible for providing clat relevant functionality.
@@ -66,23 +70,13 @@ public class ClatCoordinator {
private static final InetAddress GOOGLE_DNS_4 = InetAddress.parseNumericAddress("8.8.8.8"); private static final InetAddress GOOGLE_DNS_4 = InetAddress.parseNumericAddress("8.8.8.8");
private static final int INVALID_IFINDEX = 0; private static final int INVALID_IFINDEX = 0;
private static final int INVALID_PID = 0;
private static final long INVALID_COOKIE = 0;
@NonNull @NonNull
private final INetd mNetd; private final INetd mNetd;
@NonNull @NonNull
private final Dependencies mDeps; private final Dependencies mDeps;
@Nullable @Nullable
private String mIface = null; private ClatdTracker mClatdTracker = null;
@Nullable
private String mNat64Prefix = null;
@Nullable
private String mXlatLocalAddress4 = null;
@Nullable
private String mXlatLocalAddress6 = null;
private int mPid = INVALID_PID;
private long mCookie = INVALID_COOKIE;
@VisibleForTesting @VisibleForTesting
abstract static class Dependencies { abstract static class Dependencies {
@@ -203,6 +197,53 @@ public class ClatCoordinator {
} }
} }
@VisibleForTesting
static class ClatdTracker {
@NonNull
public final String iface;
public final int ifIndex;
@NonNull
public final String v4iface;
public final int v4ifIndex;
@NonNull
public final Inet4Address v4;
@NonNull
public final Inet6Address v6;
@NonNull
public final Inet6Address pfx96;
public final int pid;
public final long cookie;
ClatdTracker(@NonNull String iface, int ifIndex, @NonNull String v4iface,
int v4ifIndex, @NonNull Inet4Address v4, @NonNull Inet6Address v6,
@NonNull Inet6Address pfx96, int pid, long cookie) {
this.iface = iface;
this.ifIndex = ifIndex;
this.v4iface = v4iface;
this.v4ifIndex = v4ifIndex;
this.v4 = v4;
this.v6 = v6;
this.pfx96 = pfx96;
this.pid = pid;
this.cookie = cookie;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ClatdTracker)) return false;
ClatdTracker that = (ClatdTracker) o;
return Objects.equals(this.iface, that.iface)
&& this.ifIndex == that.ifIndex
&& Objects.equals(this.v4iface, that.v4iface)
&& this.v4ifIndex == that.v4ifIndex
&& Objects.equals(this.v4, that.v4)
&& Objects.equals(this.v6, that.v6)
&& Objects.equals(this.pfx96, that.pfx96)
&& this.pid == that.pid
&& this.cookie == that.cookie;
}
};
@VisibleForTesting @VisibleForTesting
static int getFwmark(int netId) { static int getFwmark(int netId) {
// See union Fwmark in system/netd/include/Fwmark.h // See union Fwmark in system/netd/include/Fwmark.h
@@ -235,30 +276,46 @@ public class ClatCoordinator {
public String clatStart(final String iface, final int netId, public String clatStart(final String iface, final int netId,
@NonNull final IpPrefix nat64Prefix) @NonNull final IpPrefix nat64Prefix)
throws IOException { throws IOException {
if (mIface != null || mPid != INVALID_PID) { if (mClatdTracker != null) {
throw new IOException("Clatd is already running on " + mIface + " (pid " + mPid + ")"); throw new IOException("Clatd is already running on " + mClatdTracker.iface
+ " (pid " + mClatdTracker.pid + ")");
} }
if (nat64Prefix.getPrefixLength() != 96) { if (nat64Prefix.getPrefixLength() != 96) {
throw new IOException("Prefix must be 96 bits long: " + nat64Prefix); 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 .. // [1] Pick an IPv4 address from 192.0.0.4, 192.0.0.5, 192.0.0.6 ..
final String v4; final String v4Str;
try { try {
v4 = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN); v4Str = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN);
} catch (IOException e) { } catch (IOException e) {
throw new IOException("no IPv4 addresses were available for clat: " + e); throw new IOException("no IPv4 addresses were available for clat: " + e);
} }
// [2] Generate a checksum-neutral IID. final Inet4Address v4;
final String pfx96 = nat64Prefix.getAddress().getHostAddress();
final String v6;
try { try {
v6 = mDeps.generateIpv6Address(iface, v4, pfx96); v4 = (Inet4Address) InetAddresses.parseNumericAddress(v4Str);
} catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
throw new IOException("Invalid IPv4 address " + v4Str);
}
// [2] Generate a checksum-neutral IID.
final String pfx96Str = nat64Prefix.getAddress().getHostAddress();
final String v6Str;
try {
v6Str = mDeps.generateIpv6Address(iface, v4Str, pfx96Str);
} catch (IOException e) { } catch (IOException e) {
throw new IOException("no IPv6 addresses were available for clat: " + e); throw new IOException("no IPv6 addresses were available for clat: " + e);
} }
final Inet6Address pfx96 = (Inet6Address) nat64Prefix.getAddress();
final Inet6Address v6;
try {
v6 = (Inet6Address) InetAddresses.parseNumericAddress(v6Str);
} catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
throw new IOException("Invalid IPv6 address " + v6Str);
}
// [3] Open, configure and bring up the tun interface. // [3] Open, configure and bring up the tun interface.
// Create the v4-... tun interface. // Create the v4-... tun interface.
final String tunIface = CLAT_PREFIX + iface; final String tunIface = CLAT_PREFIX + iface;
@@ -269,6 +326,12 @@ public class ClatCoordinator {
throw new IOException("Create tun interface " + tunIface + " failed: " + e); throw new IOException("Create tun interface " + tunIface + " failed: " + e);
} }
final int tunIfIndex = mDeps.getInterfaceIndex(tunIface);
if (tunIfIndex == INVALID_IFINDEX) {
tunFd.close();
throw new IOException("Fail to get interface index for interface " + tunIface);
}
// disable IPv6 on it - failing to do so is not a critical error // disable IPv6 on it - failing to do so is not a critical error
try { try {
mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */); mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */);
@@ -279,7 +342,7 @@ public class ClatCoordinator {
// Detect ipv4 mtu. // Detect ipv4 mtu.
final Integer fwmark = getFwmark(netId); final Integer fwmark = getFwmark(netId);
final int detectedMtu = mDeps.detectMtu(pfx96, final int detectedMtu = mDeps.detectMtu(pfx96Str,
ByteBuffer.wrap(GOOGLE_DNS_4.getAddress()).getInt(), fwmark); ByteBuffer.wrap(GOOGLE_DNS_4.getAddress()).getInt(), fwmark);
final int mtu = adjustMtu(detectedMtu); final int mtu = adjustMtu(detectedMtu);
Log.i(TAG, "ipv4 mtu is " + mtu); Log.i(TAG, "ipv4 mtu is " + mtu);
@@ -295,7 +358,7 @@ public class ClatCoordinator {
} }
final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel(); final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
ifConfig.ifName = tunIface; ifConfig.ifName = tunIface;
ifConfig.ipv4Addr = v4; ifConfig.ipv4Addr = v4Str;
ifConfig.prefixLength = 32; ifConfig.prefixLength = 32;
ifConfig.hwAddr = ""; ifConfig.hwAddr = "";
ifConfig.flags = new String[] {IF_STATE_UP}; ifConfig.flags = new String[] {IF_STATE_UP};
@@ -333,8 +396,8 @@ public class ClatCoordinator {
throw new IOException("Open raw socket failed: " + e); throw new IOException("Open raw socket failed: " + e);
} }
final int ifaceIndex = mDeps.getInterfaceIndex(iface); final int ifIndex = mDeps.getInterfaceIndex(iface);
if (ifaceIndex == INVALID_IFINDEX) { if (ifIndex == INVALID_IFINDEX) {
tunFd.close(); tunFd.close();
readSock6.close(); readSock6.close();
writeSock6.close(); writeSock6.close();
@@ -343,7 +406,7 @@ public class ClatCoordinator {
// Start translating packets to the new prefix. // Start translating packets to the new prefix.
try { try {
mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6, ifaceIndex); mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6Str, ifIndex);
} catch (IOException e) { } catch (IOException e) {
tunFd.close(); tunFd.close();
readSock6.close(); readSock6.close();
@@ -352,7 +415,7 @@ public class ClatCoordinator {
} }
// Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting. // Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting.
long cookie; final long cookie;
try { try {
cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor()); cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor());
} catch (IOException e) { } catch (IOException e) {
@@ -364,7 +427,7 @@ public class ClatCoordinator {
// Update our packet socket filter to reflect the new 464xlat IP address. // Update our packet socket filter to reflect the new 464xlat IP address.
try { try {
mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6, ifaceIndex); mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6Str, ifIndex);
} catch (IOException e) { } catch (IOException e) {
tunFd.close(); tunFd.close();
readSock6.close(); readSock6.close();
@@ -373,15 +436,12 @@ public class ClatCoordinator {
} }
// [5] Start clatd. // [5] Start clatd.
final int pid;
try { try {
mPid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(), pid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(),
writeSock6.getFileDescriptor(), iface, pfx96, v4, v6); writeSock6.getFileDescriptor(), iface, pfx96Str, v4Str, v6Str);
mIface = iface;
mNat64Prefix = pfx96;
mXlatLocalAddress4 = v4;
mXlatLocalAddress6 = v6;
mCookie = cookie;
} catch (IOException e) { } catch (IOException e) {
// TODO: probably refactor to handle the exception of #untagSocket if any.
mDeps.untagSocket(cookie); mDeps.untagSocket(cookie);
throw new IOException("Error start clatd on " + iface + ": " + e); throw new IOException("Error start clatd on " + iface + ": " + e);
} finally { } finally {
@@ -390,29 +450,38 @@ public class ClatCoordinator {
writeSock6.close(); writeSock6.close();
} }
return v6; // [6] Initialize and store clatd tracker object.
mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96,
pid, cookie);
return v6Str;
} }
/** /**
* Stop clatd * Stop clatd
*/ */
public void clatStop() throws IOException { public void clatStop() throws IOException {
if (mPid == INVALID_PID) { if (mClatdTracker == null) {
throw new IOException("Clatd has not started"); throw new IOException("Clatd has not started");
} }
Log.i(TAG, "Stopping clatd pid=" + mPid + " on " + mIface); Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface);
mDeps.stopClatd(mIface, mNat64Prefix, mXlatLocalAddress4, mXlatLocalAddress6, mPid); mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(),
mDeps.untagSocket(mCookie); mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(),
mClatdTracker.pid);
mDeps.untagSocket(mClatdTracker.cookie);
Log.i(TAG, "clatd on " + mIface + " stopped"); Log.i(TAG, "clatd on " + mClatdTracker.iface + " stopped");
mClatdTracker = null;
}
mIface = null; /**
mNat64Prefix = null; * Get clatd tracker. For test only.
mXlatLocalAddress4 = null; */
mXlatLocalAddress6 = null; @VisibleForTesting
mPid = INVALID_PID; @Nullable
mCookie = INVALID_COOKIE; ClatdTracker getClatdTrackerForTesting() {
return mClatdTracker;
} }
private static native String native_selectIpv4Address(String v4addr, int prefixlen) private static native String native_selectIpv4Address(String v4addr, int prefixlen)

View File

@@ -25,6 +25,7 @@ import static com.android.server.connectivity.ClatCoordinator.INIT_V4ADDR_STRING
import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
@@ -33,6 +34,7 @@ import static org.mockito.Mockito.inOrder;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.net.INetd; import android.net.INetd;
import android.net.InetAddresses;
import android.net.IpPrefix; import android.net.IpPrefix;
import android.os.Build; import android.os.Build;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
@@ -52,6 +54,8 @@ import org.mockito.Spy;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Objects; import java.util.Objects;
@RunWith(DevSdkIgnoreRunner.class) @RunWith(DevSdkIgnoreRunner.class)
@@ -61,9 +65,12 @@ public class ClatCoordinatorTest {
private static final String BASE_IFACE = "test0"; private static final String BASE_IFACE = "test0";
private static final String STACKED_IFACE = "v4-test0"; private static final String STACKED_IFACE = "v4-test0";
private static final int BASE_IFINDEX = 1000; private static final int BASE_IFINDEX = 1000;
private static final int STACKED_IFINDEX = 1001;
private static final IpPrefix NAT64_IP_PREFIX = new IpPrefix("64:ff9b::/96"); private static final IpPrefix NAT64_IP_PREFIX = new IpPrefix("64:ff9b::/96");
private static final String NAT64_PREFIX_STRING = "64:ff9b::"; private static final String NAT64_PREFIX_STRING = "64:ff9b::";
private static final Inet6Address INET6_PFX96 = (Inet6Address)
InetAddresses.parseNumericAddress(NAT64_PREFIX_STRING);
private static final int GOOGLE_DNS_4 = 0x08080808; // 8.8.8.8 private static final int GOOGLE_DNS_4 = 0x08080808; // 8.8.8.8
private static final int NETID = 42; private static final int NETID = 42;
@@ -74,6 +81,10 @@ public class ClatCoordinatorTest {
private static final String XLAT_LOCAL_IPV4ADDR_STRING = "192.0.0.46"; private static final String XLAT_LOCAL_IPV4ADDR_STRING = "192.0.0.46";
private static final String XLAT_LOCAL_IPV6ADDR_STRING = "2001:db8:0:b11::464"; private static final String XLAT_LOCAL_IPV6ADDR_STRING = "2001:db8:0:b11::464";
private static final Inet4Address INET4_LOCAL4 = (Inet4Address)
InetAddresses.parseNumericAddress(XLAT_LOCAL_IPV4ADDR_STRING);
private static final Inet6Address INET6_LOCAL6 = (Inet6Address)
InetAddresses.parseNumericAddress(XLAT_LOCAL_IPV6ADDR_STRING);
private static final int CLATD_PID = 10483; private static final int CLATD_PID = 10483;
private static final int TUN_FD = 534; private static final int TUN_FD = 534;
@@ -129,6 +140,8 @@ public class ClatCoordinatorTest {
public int getInterfaceIndex(String ifName) { public int getInterfaceIndex(String ifName) {
if (BASE_IFACE.equals(ifName)) { if (BASE_IFACE.equals(ifName)) {
return BASE_IFINDEX; return BASE_IFINDEX;
} else if (STACKED_IFACE.equals(ifName)) {
return STACKED_IFINDEX;
} }
fail("unsupported arg: " + ifName); fail("unsupported arg: " + ifName);
return -1; return -1;
@@ -315,6 +328,11 @@ public class ClatCoordinatorTest {
// [1] Start clatd. // [1] Start clatd.
final String addr6For464xlat = coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX); final String addr6For464xlat = coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX);
assertEquals(XLAT_LOCAL_IPV6ADDR_STRING, addr6For464xlat); assertEquals(XLAT_LOCAL_IPV6ADDR_STRING, addr6For464xlat);
final ClatCoordinator.ClatdTracker expected = new ClatCoordinator.ClatdTracker(
BASE_IFACE, BASE_IFINDEX, STACKED_IFACE, STACKED_IFINDEX,
INET4_LOCAL4, INET6_LOCAL6, INET6_PFX96, CLATD_PID, RAW_SOCK_COOKIE);
final ClatCoordinator.ClatdTracker actual = coordinator.getClatdTrackerForTesting();
assertEquals(expected, actual);
// Pick an IPv4 address. // Pick an IPv4 address.
inOrder.verify(mDeps).selectIpv4Address(eq(INIT_V4ADDR_STRING), inOrder.verify(mDeps).selectIpv4Address(eq(INIT_V4ADDR_STRING),
@@ -327,6 +345,7 @@ public class ClatCoordinatorTest {
// Open, configure and bring up the tun interface. // Open, configure and bring up the tun interface.
inOrder.verify(mDeps).createTunInterface(eq(STACKED_IFACE)); inOrder.verify(mDeps).createTunInterface(eq(STACKED_IFACE));
inOrder.verify(mDeps).adoptFd(eq(TUN_FD)); inOrder.verify(mDeps).adoptFd(eq(TUN_FD));
inOrder.verify(mDeps).getInterfaceIndex(eq(STACKED_IFACE));
inOrder.verify(mNetd).interfaceSetEnableIPv6(eq(STACKED_IFACE), eq(false /* enable */)); inOrder.verify(mNetd).interfaceSetEnableIPv6(eq(STACKED_IFACE), eq(false /* enable */));
inOrder.verify(mDeps).detectMtu(eq(NAT64_PREFIX_STRING), eq(GOOGLE_DNS_4), eq(MARK)); inOrder.verify(mDeps).detectMtu(eq(NAT64_PREFIX_STRING), eq(GOOGLE_DNS_4), eq(MARK));
inOrder.verify(mNetd).interfaceSetMtu(eq(STACKED_IFACE), inOrder.verify(mNetd).interfaceSetMtu(eq(STACKED_IFACE),
@@ -372,6 +391,7 @@ public class ClatCoordinatorTest {
inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING), inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID)); eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE)); inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE));
assertNull(coordinator.getClatdTrackerForTesting());
inOrder.verifyNoMoreInteractions(); inOrder.verifyNoMoreInteractions();
// [4] Expect an IO exception while stopping a clatd that doesn't exist. // [4] Expect an IO exception while stopping a clatd that doesn't exist.