Merge "Add bpf forwording packet count test"

This commit is contained in:
Treehugger Robot
2022-04-20 11:37:18 +00:00
committed by Gerrit Code Review
2 changed files with 76 additions and 16 deletions

View File

@@ -121,6 +121,7 @@ public class BpfCoordinator {
private static final String TETHER_LIMIT_MAP_PATH = makeMapPath("limit"); private static final String TETHER_LIMIT_MAP_PATH = makeMapPath("limit");
private static final String TETHER_ERROR_MAP_PATH = makeMapPath("error"); private static final String TETHER_ERROR_MAP_PATH = makeMapPath("error");
private static final String TETHER_DEV_MAP_PATH = makeMapPath("dev"); private static final String TETHER_DEV_MAP_PATH = makeMapPath("dev");
private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4"; private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
// Using "," as a separator is safe because base64 characters are [0-9a-zA-Z/=+]. // Using "," as a separator is safe because base64 characters are [0-9a-zA-Z/=+].
@@ -1088,14 +1089,14 @@ public class BpfCoordinator {
return keyBase64Str + DUMP_BASE64_DELIMITER + valueBase64Str; return keyBase64Str + DUMP_BASE64_DELIMITER + valueBase64Str;
} }
private void dumpRawIpv4ForwardingRuleMap( private <K extends Struct, V extends Struct> void dumpRawMap(BpfMap<K, V> map,
BpfMap<Tether4Key, Tether4Value> map, IndentingPrintWriter pw) throws ErrnoException { IndentingPrintWriter pw) throws ErrnoException {
if (map == null) { if (map == null) {
pw.println("No IPv4 support"); pw.println("No BPF support");
return; return;
} }
if (map.isEmpty()) { if (map.isEmpty()) {
pw.println("No rules"); pw.println("No entries");
return; return;
} }
map.forEach((k, v) -> pw.println(bpfMapEntryToBase64String(k, v))); map.forEach((k, v) -> pw.println(bpfMapEntryToBase64String(k, v)));
@@ -1112,9 +1113,17 @@ public class BpfCoordinator {
// it is okay for now because this is used by test only and test is supposed to use // it is okay for now because this is used by test only and test is supposed to use
// expected argument order. // expected argument order.
// TODO: dump downstream4 map. // TODO: dump downstream4 map.
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_STATS)) {
try (BpfMap<TetherStatsKey, TetherStatsValue> statsMap = mDeps.getBpfStatsMap()) {
dumpRawMap(statsMap, pw);
} catch (ErrnoException e) {
pw.println("Error dumping stats map: " + e);
}
return;
}
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) { if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) {
try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) { try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
dumpRawIpv4ForwardingRuleMap(upstreamMap, pw); dumpRawMap(upstreamMap, pw);
} catch (ErrnoException e) { } catch (ErrnoException e) {
pw.println("Error dumping IPv4 map: " + e); pw.println("Error dumping IPv4 map: " + e);
} }

View File

@@ -74,6 +74,8 @@ import com.android.net.module.util.PacketBuilder;
import com.android.net.module.util.Struct; import com.android.net.module.util.Struct;
import com.android.net.module.util.bpf.Tether4Key; import com.android.net.module.util.bpf.Tether4Key;
import com.android.net.module.util.bpf.Tether4Value; import com.android.net.module.util.bpf.Tether4Value;
import com.android.net.module.util.bpf.TetherStatsKey;
import com.android.net.module.util.bpf.TetherStatsValue;
import com.android.net.module.util.structs.EthernetHeader; import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header; import com.android.net.module.util.structs.Icmpv6Header;
import com.android.net.module.util.structs.Ipv4Header; import com.android.net.module.util.structs.Ipv4Header;
@@ -128,6 +130,13 @@ public class EthernetTetheringTest {
// Kernel treats a confirmed UDP connection which active after two seconds as stream mode. // Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
// See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5. // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
private static final int UDP_STREAM_TS_MS = 2000; private static final int UDP_STREAM_TS_MS = 2000;
// Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
private static final int RX_UDP_PACKET_SIZE = 30;
private static final int RX_UDP_PACKET_COUNT = 456;
// Per TX UDP packet size: ethhdr (14) + iphdr (20) + udphdr (8) + payload (2) = 44 bytes.
private static final int TX_UDP_PACKET_SIZE = 44;
private static final int TX_UDP_PACKET_COUNT = 123;
private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8"); private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8");
private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64"); private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8"); private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
@@ -136,6 +145,7 @@ public class EthernetTetheringTest {
ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa }); ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa });
private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap"; 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"; private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
private static final String BASE64_DELIMITER = ","; private static final String BASE64_DELIMITER = ",";
private static final String LINE_DELIMITER = "\\n"; private static final String LINE_DELIMITER = "\\n";
@@ -957,6 +967,7 @@ public class EthernetTetheringTest {
return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD3); return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD3);
}); });
// [1] Verify IPv4 upstream rule map.
final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump( final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4); Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
assertNotNull(upstreamMap); assertNotNull(upstreamMap);
@@ -965,20 +976,60 @@ public class EthernetTetheringTest {
final Map.Entry<Tether4Key, Tether4Value> rule = final Map.Entry<Tether4Key, Tether4Value> rule =
upstreamMap.entrySet().iterator().next(); upstreamMap.entrySet().iterator().next();
final Tether4Key key = rule.getKey(); final Tether4Key upstream4Key = rule.getKey();
assertEquals(IPPROTO_UDP, key.l4proto); assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), key.src4)); assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
assertEquals(LOCAL_PORT, key.srcPort); assertEquals(LOCAL_PORT, upstream4Key.srcPort);
assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), key.dst4)); assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
assertEquals(REMOTE_PORT, key.dstPort); assertEquals(REMOTE_PORT, upstream4Key.dstPort);
final Tether4Value value = rule.getValue(); final Tether4Value upstream4Value = rule.getValue();
assertTrue(Arrays.equals(publicIp4Addr.getAddress(), assertTrue(Arrays.equals(publicIp4Addr.getAddress(),
InetAddress.getByAddress(value.src46).getAddress())); InetAddress.getByAddress(upstream4Value.src46).getAddress()));
assertEquals(LOCAL_PORT, value.srcPort); assertEquals(LOCAL_PORT, upstream4Value.srcPort);
assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
InetAddress.getByAddress(value.dst46).getAddress())); InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
assertEquals(REMOTE_PORT, value.dstPort); assertEquals(REMOTE_PORT, upstream4Value.dstPort);
// [2] Verify stats map.
// Transmit packets on both direction for verifying stats. Because we only care the
// packet count in stats test, we just reuse the existing packets to increaes
// the packet count on both direction.
// Send packets on original direction.
for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
tester.verifyUpload(remote, originalPacket, p -> {
Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD);
});
}
// Send packets on reply direction.
for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
remote.verifyDownload(tester, replyPacket, p -> {
Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
return isExpectedUdpPacket(p, true/* hasEther */, PAYLOAD2);
});
}
// Dump stats map to verify.
final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
assertNotNull(statsMap);
assertEquals(1, statsMap.size());
final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
statsMap.entrySet().iterator().next();
// TODO: verify the upstream index in TetherStatsKey.
final TetherStatsValue statsValue = stats.getValue();
assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
assertEquals(0, statsValue.rxErrors);
assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
assertEquals(0, statsValue.txErrors);
} }
} }