From 2ab2a1b0b0a11bdfd0de01ab5a1ac0057830e25a Mon Sep 17 00:00:00 2001 From: KH Shi Date: Wed, 21 Jun 2023 20:27:20 +0800 Subject: [PATCH] Add testTetherClatBpfOffloadUdp to TetheringTest Add a new test case to TetheringTest. It verifies upstream and downstream UDP packets are actually processed by Clatd's BPF offload. Bug: 285124667 Test: atest TetheringIntegrationTests Change-Id: I2f0c3953499af7a701c1b7e72830d502efbd31a1 --- .../android/net/EthernetTetheringTest.java | 110 ++++++++++++++++-- 1 file changed, 103 insertions(+), 7 deletions(-) diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 9cdba2f6aa..c8aab88467 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -33,6 +33,8 @@ import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA; import static com.android.net.module.util.HexDump.dumpHexString; import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE; import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; +import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN; +import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; import static com.android.testutils.DeviceInfoUtils.KVersion; import static com.android.testutils.TestPermissionUtil.runAsShell; @@ -62,6 +64,10 @@ import androidx.test.runner.AndroidJUnit4; import com.android.net.module.util.BpfDump; import com.android.net.module.util.Ipv6Utils; import com.android.net.module.util.Struct; +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.net.module.util.bpf.Tether4Key; import com.android.net.module.util.bpf.Tether4Value; import com.android.net.module.util.bpf.TetherStatsKey; @@ -122,6 +128,8 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { private static final int TX_UDP_PACKET_SIZE = 30; private static final int TX_UDP_PACKET_COUNT = 123; + private static final String DUMPSYS_CLAT_RAWMAP_EGRESS4_ARG = "clatEgress4RawBpfMap"; + private static final String DUMPSYS_CLAT_RAWMAP_INGRESS6_ARG = "clatIngress6RawBpfMap"; private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap"; private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats"; private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4"; @@ -901,11 +909,10 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { @NonNull private HashMap dumpAndParseRawMap( - Class keyClass, Class valueClass, @NonNull String mapArg) + Class keyClass, Class valueClass, @NonNull String service, @NonNull String[] args) throws Exception { - final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg}; final String rawMapStr = runAsShell(DUMP, () -> - DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args)); + DumpTestUtils.dumpService(service, args)); final HashMap map = new HashMap<>(); for (final String line : rawMapStr.split(LINE_DELIMITER)) { @@ -918,10 +925,10 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { @Nullable private HashMap pollRawMapFromDump( - Class keyClass, Class valueClass, @NonNull String mapArg) + Class keyClass, Class valueClass, @NonNull String service, @NonNull String[] args) throws Exception { for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) { - final HashMap map = dumpAndParseRawMap(keyClass, valueClass, mapArg); + final HashMap map = dumpAndParseRawMap(keyClass, valueClass, service, args); if (!map.isEmpty()) return map; Thread.sleep(DUMP_POLLING_INTERVAL_MS); @@ -977,8 +984,10 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { Thread.sleep(UDP_STREAM_SLACK_MS); // [1] Verify IPv4 upstream rule map. + final String[] upstreamArgs = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, + DUMPSYS_RAWMAP_ARG_UPSTREAM4}; final HashMap upstreamMap = pollRawMapFromDump( - Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4); + Tether4Key.class, Tether4Value.class, Context.TETHERING_SERVICE, upstreamArgs); assertNotNull(upstreamMap); assertEquals(1, upstreamMap.size()); @@ -1017,8 +1026,10 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { } // Dump stats map to verify. + final String[] statsArgs = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, + DUMPSYS_RAWMAP_ARG_STATS}; final HashMap statsMap = pollRawMapFromDump( - TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS); + TetherStatsKey.class, TetherStatsValue.class, Context.TETHERING_SERVICE, statsArgs); assertNotNull(statsMap); assertEquals(1, statsMap.size()); @@ -1053,4 +1064,89 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { runUdp4Test(); } + + private ClatEgress4Value getClatEgress4Value() throws Exception { + // Command: dumpsys connectivity clatEgress4RawBpfMap + final String[] args = new String[] {DUMPSYS_CLAT_RAWMAP_EGRESS4_ARG}; + final HashMap egress4Map = pollRawMapFromDump( + ClatEgress4Key.class, ClatEgress4Value.class, Context.CONNECTIVITY_SERVICE, args); + assertNotNull(egress4Map); + assertEquals(1, egress4Map.size()); + return egress4Map.entrySet().iterator().next().getValue(); + } + + private ClatIngress6Value getClatIngress6Value() throws Exception { + // Command: dumpsys connectivity clatIngress6RawBpfMap + final String[] args = new String[] {DUMPSYS_CLAT_RAWMAP_INGRESS6_ARG}; + final HashMap ingress6Map = pollRawMapFromDump( + ClatIngress6Key.class, ClatIngress6Value.class, Context.CONNECTIVITY_SERVICE, args); + assertNotNull(ingress6Map); + assertEquals(1, ingress6Map.size()); + return ingress6Map.entrySet().iterator().next().getValue(); + } + + /** + * Test network topology: + * + * public network (rawip) private network + * | UE (CLAT support) | + * +---------------+ V +------------+------------+ V +------------+ + * | NAT64 Gateway +---------+ Upstream | Downstream +---------+ Client | + * +---------------+ +------------+------------+ +------------+ + * remote ip public ip private ip + * [64:ff9b::808:808]:443 [clat ipv6]:9876 [TetheredDevice ipv4]:9876 + * + * Note that CLAT IPv6 address is generated by ClatCoordinator. Get the CLAT IPv6 address by + * sending out an IPv4 packet and extracting the source address from CLAT translated IPv6 + * packet. + */ + @Test + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + public void testTetherClatBpfOffloadUdp() throws Exception { + assumeKernelSupportBpfOffloadUdpV4(); + + // CLAT only starts on IPv6 only network. + final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), + toList(TEST_IP6_DNS)); + final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); + + // Get CLAT IPv6 address. + final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); + + // Get current values before sending packets. + final ClatEgress4Value oldEgress4 = getClatEgress4Value(); + final ClatIngress6Value oldIngress6 = getClatIngress6Value(); + + // Send an IPv4 UDP packet in original direction. + // IPv4 packet -- CLAT translation --> IPv6 packet + for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) { + sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, tethered.ipv4Addr, + REMOTE_IP4_ADDR, tester, true /* is4To6 */); + } + + // Send an IPv6 UDP packet in reply direction. + // IPv6 packet -- CLAT translation --> IPv4 packet + for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) { + sendDownloadPacketUdp(REMOTE_NAT64_ADDR, clatIp6, tester, true /* is6To4 */); + } + + // After sending test packets, get stats again to verify their differences. + final ClatEgress4Value newEgress4 = getClatEgress4Value(); + final ClatIngress6Value newIngress6 = getClatIngress6Value(); + + assertEquals(RX_UDP_PACKET_COUNT, newIngress6.packets - oldIngress6.packets); + assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, + newIngress6.bytes - oldIngress6.bytes); + assertEquals(TX_UDP_PACKET_COUNT, newEgress4.packets - oldEgress4.packets); + // The increase in egress traffic equals the expected size of the translated UDP packets. + // Calculation: + // - Original UDP packet was TX_UDP_PACKET_SIZE bytes (IPv4 header + UDP header + payload). + // - After CLAT translation, each packet is now: + // IPv6 header + unchanged UDP header + unchanged payload + // Therefore, the total size of the translated UDP packet should be: + // TX_UDP_PACKET_SIZE + IPV6_HEADER_LEN - IPV4_HEADER_MIN_LEN + assertEquals( + TX_UDP_PACKET_COUNT * (TX_UDP_PACKET_SIZE + IPV6_HEADER_LEN - IPV4_HEADER_MIN_LEN), + newEgress4.bytes - oldEgress4.bytes); + } }