diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java index c1a8195b28..2e26ae4f18 100644 --- a/service/src/com/android/server/connectivity/ClatCoordinator.java +++ b/service/src/com/android/server/connectivity/ClatCoordinator.java @@ -24,6 +24,7 @@ import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.INetd; +import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.os.ParcelFileDescriptor; @@ -36,8 +37,11 @@ import com.android.net.module.util.InterfaceParams; import java.io.FileDescriptor; import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; +import java.util.Objects; /** * 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 int INVALID_IFINDEX = 0; - private static final int INVALID_PID = 0; - private static final long INVALID_COOKIE = 0; @NonNull private final INetd mNetd; @NonNull private final Dependencies mDeps; @Nullable - private String mIface = 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; + private ClatdTracker mClatdTracker = null; @VisibleForTesting 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 static int getFwmark(int netId) { // 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, @NonNull final IpPrefix nat64Prefix) throws IOException { - if (mIface != null || mPid != INVALID_PID) { - throw new IOException("Clatd is already running on " + mIface + " (pid " + mPid + ")"); + if (mClatdTracker != null) { + throw new IOException("Clatd is already running on " + mClatdTracker.iface + + " (pid " + mClatdTracker.pid + ")"); } 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; + final String v4Str; try { - v4 = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN); + v4Str = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN); } catch (IOException e) { throw new IOException("no IPv4 addresses were available for clat: " + e); } - // [2] Generate a checksum-neutral IID. - final String pfx96 = nat64Prefix.getAddress().getHostAddress(); - final String v6; + final Inet4Address v4; 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) { 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. // Create the v4-... tun interface. final String tunIface = CLAT_PREFIX + iface; @@ -269,6 +326,12 @@ public class ClatCoordinator { 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 try { mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */); @@ -279,7 +342,7 @@ public class ClatCoordinator { // Detect ipv4 mtu. 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); final int mtu = adjustMtu(detectedMtu); Log.i(TAG, "ipv4 mtu is " + mtu); @@ -295,7 +358,7 @@ public class ClatCoordinator { } final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel(); ifConfig.ifName = tunIface; - ifConfig.ipv4Addr = v4; + ifConfig.ipv4Addr = v4Str; ifConfig.prefixLength = 32; ifConfig.hwAddr = ""; ifConfig.flags = new String[] {IF_STATE_UP}; @@ -333,8 +396,8 @@ public class ClatCoordinator { throw new IOException("Open raw socket failed: " + e); } - final int ifaceIndex = mDeps.getInterfaceIndex(iface); - if (ifaceIndex == INVALID_IFINDEX) { + final int ifIndex = mDeps.getInterfaceIndex(iface); + if (ifIndex == INVALID_IFINDEX) { tunFd.close(); readSock6.close(); writeSock6.close(); @@ -343,7 +406,7 @@ public class ClatCoordinator { // Start translating packets to the new prefix. try { - mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6, ifaceIndex); + mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6Str, ifIndex); } catch (IOException e) { tunFd.close(); readSock6.close(); @@ -352,7 +415,7 @@ public class ClatCoordinator { } // Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting. - long cookie; + final long cookie; try { cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor()); } catch (IOException e) { @@ -364,7 +427,7 @@ public class ClatCoordinator { // Update our packet socket filter to reflect the new 464xlat IP address. try { - mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6, ifaceIndex); + mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6Str, ifIndex); } catch (IOException e) { tunFd.close(); readSock6.close(); @@ -373,15 +436,12 @@ public class ClatCoordinator { } // [5] Start clatd. + final int pid; try { - mPid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(), - writeSock6.getFileDescriptor(), iface, pfx96, v4, v6); - mIface = iface; - mNat64Prefix = pfx96; - mXlatLocalAddress4 = v4; - mXlatLocalAddress6 = v6; - mCookie = cookie; + 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); throw new IOException("Error start clatd on " + iface + ": " + e); } finally { @@ -390,29 +450,38 @@ public class ClatCoordinator { 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 */ public void clatStop() throws IOException { - if (mPid == INVALID_PID) { + if (mClatdTracker == null) { 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.untagSocket(mCookie); + mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(), + 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; - mXlatLocalAddress4 = null; - mXlatLocalAddress6 = null; - mPid = INVALID_PID; - mCookie = INVALID_COOKIE; + /** + * Get clatd tracker. For test only. + */ + @VisibleForTesting + @Nullable + ClatdTracker getClatdTrackerForTesting() { + return mClatdTracker; } private static native String native_selectIpv4Address(String v4addr, int prefixlen) diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java index 8a2cfc2da8..6c8b545b03 100644 --- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java +++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java @@ -25,6 +25,7 @@ import static com.android.server.connectivity.ClatCoordinator.INIT_V4ADDR_STRING import static com.android.testutils.MiscAsserts.assertThrows; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.clearInvocations; @@ -33,6 +34,7 @@ import static org.mockito.Mockito.inOrder; import android.annotation.NonNull; import android.net.INetd; +import android.net.InetAddresses; import android.net.IpPrefix; import android.os.Build; import android.os.ParcelFileDescriptor; @@ -52,6 +54,8 @@ import org.mockito.Spy; import java.io.FileDescriptor; import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.util.Objects; @RunWith(DevSdkIgnoreRunner.class) @@ -61,9 +65,12 @@ public class ClatCoordinatorTest { private static final String BASE_IFACE = "test0"; private static final String STACKED_IFACE = "v4-test0"; 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 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 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_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 TUN_FD = 534; @@ -129,6 +140,8 @@ public class ClatCoordinatorTest { public int getInterfaceIndex(String ifName) { if (BASE_IFACE.equals(ifName)) { return BASE_IFINDEX; + } else if (STACKED_IFACE.equals(ifName)) { + return STACKED_IFINDEX; } fail("unsupported arg: " + ifName); return -1; @@ -315,6 +328,11 @@ public class ClatCoordinatorTest { // [1] Start clatd. final String addr6For464xlat = coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX); 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. inOrder.verify(mDeps).selectIpv4Address(eq(INIT_V4ADDR_STRING), @@ -327,6 +345,7 @@ public class ClatCoordinatorTest { // Open, configure and bring up the tun interface. inOrder.verify(mDeps).createTunInterface(eq(STACKED_IFACE)); 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(mDeps).detectMtu(eq(NAT64_PREFIX_STRING), eq(GOOGLE_DNS_4), eq(MARK)); 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), eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID)); inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE)); + assertNull(coordinator.getClatdTrackerForTesting()); inOrder.verifyNoMoreInteractions(); // [4] Expect an IO exception while stopping a clatd that doesn't exist.