BpfCoordinator: Add an option to choose which raw bpf map to dump
Make that tethering dumpsys can choose which map to dump. The related functions in BpfCoordinator and its test are refactored into template functions. This is used in the following commits which will dump stats map for testing. Bug: 227159997 Test: atest TetheringCoverageTests Change-Id: I23d747543d16a971773c132b60227eb3f42efe25
This commit is contained in:
@@ -65,6 +65,7 @@ import com.android.net.module.util.BpfMap;
|
|||||||
import com.android.net.module.util.CollectionUtils;
|
import com.android.net.module.util.CollectionUtils;
|
||||||
import com.android.net.module.util.InterfaceParams;
|
import com.android.net.module.util.InterfaceParams;
|
||||||
import com.android.net.module.util.NetworkStackConstants;
|
import com.android.net.module.util.NetworkStackConstants;
|
||||||
|
import com.android.net.module.util.Struct;
|
||||||
import com.android.net.module.util.Struct.U32;
|
import com.android.net.module.util.Struct.U32;
|
||||||
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;
|
||||||
@@ -120,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_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/=+].
|
||||||
private static final String DUMP_BASE64_DELIMITER = ",";
|
private static final String DUMP_BASE64_DELIMITER = ",";
|
||||||
@@ -1074,7 +1076,8 @@ public class BpfCoordinator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String ipv4RuleToBase64String(Tether4Key key, Tether4Value value) {
|
private <K extends Struct, V extends Struct> String bpfMapEntryToBase64String(
|
||||||
|
final K key, final V value) {
|
||||||
final byte[] keyBytes = key.writeToBytes();
|
final byte[] keyBytes = key.writeToBytes();
|
||||||
final String keyBase64Str = Base64.encodeToString(keyBytes, Base64.DEFAULT)
|
final String keyBase64Str = Base64.encodeToString(keyBytes, Base64.DEFAULT)
|
||||||
.replace("\n", "");
|
.replace("\n", "");
|
||||||
@@ -1095,18 +1098,27 @@ public class BpfCoordinator {
|
|||||||
pw.println("No rules");
|
pw.println("No rules");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
map.forEach((k, v) -> pw.println(ipv4RuleToBase64String(k, v)));
|
map.forEach((k, v) -> pw.println(bpfMapEntryToBase64String(k, v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump raw BPF map in base64 encoded strings. For test only.
|
* Dump raw BPF map in base64 encoded strings. For test only.
|
||||||
|
* Only allow to dump one map path once.
|
||||||
|
* Format:
|
||||||
|
* $ dumpsys tethering bpfRawMap --<map name>
|
||||||
*/
|
*/
|
||||||
public void dumpRawMap(@NonNull IndentingPrintWriter pw) {
|
public void dumpRawMap(@NonNull IndentingPrintWriter pw, @Nullable String[] args) {
|
||||||
try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
|
// TODO: consider checking the arg order that <map name> is after "bpfRawMap". Probably
|
||||||
// TODO: dump downstream map.
|
// it is okay for now because this is used by test only and test is supposed to use
|
||||||
dumpRawIpv4ForwardingRuleMap(upstreamMap, pw);
|
// expected argument order.
|
||||||
} catch (ErrnoException e) {
|
// TODO: dump downstream4 map.
|
||||||
pw.println("Error dumping IPv4 map: " + e);
|
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) {
|
||||||
|
try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
|
||||||
|
dumpRawIpv4ForwardingRuleMap(upstreamMap, pw);
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
pw.println("Error dumping IPv4 map: " + e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2513,9 +2513,8 @@ public class Tethering {
|
|||||||
writer, " ");
|
writer, " ");
|
||||||
|
|
||||||
// Used for testing instead of human debug.
|
// Used for testing instead of human debug.
|
||||||
// TODO: add options to choose which map to dump.
|
|
||||||
if (argsContain(args, "bpfRawMap")) {
|
if (argsContain(args, "bpfRawMap")) {
|
||||||
mBpfCoordinator.dumpRawMap(pw);
|
mBpfCoordinator.dumpRawMap(pw, args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,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_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";
|
||||||
|
|
||||||
@@ -956,7 +957,8 @@ public class EthernetTetheringTest {
|
|||||||
return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD3);
|
return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD3);
|
||||||
});
|
});
|
||||||
|
|
||||||
final HashMap<Tether4Key, Tether4Value> upstreamMap = pollIpv4UpstreamMapFromDump();
|
final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
|
||||||
|
Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
|
||||||
assertNotNull(upstreamMap);
|
assertNotNull(upstreamMap);
|
||||||
assertEquals(1, upstreamMap.size());
|
assertEquals(1, upstreamMap.size());
|
||||||
|
|
||||||
@@ -1021,7 +1023,8 @@ public class EthernetTetheringTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Pair<Tether4Key, Tether4Value> parseTether4KeyValue(@NonNull String dumpStr) {
|
private <K extends Struct, V extends Struct> Pair<K, V> parseMapKeyValue(
|
||||||
|
Class<K> keyClass, Class<V> valueClass, @NonNull String dumpStr) {
|
||||||
Log.w(TAG, "Parsing string: " + dumpStr);
|
Log.w(TAG, "Parsing string: " + dumpStr);
|
||||||
|
|
||||||
String[] keyValueStrs = dumpStr.split(BASE64_DELIMITER);
|
String[] keyValueStrs = dumpStr.split(BASE64_DELIMITER);
|
||||||
@@ -1034,36 +1037,38 @@ public class EthernetTetheringTest {
|
|||||||
Log.d(TAG, "keyBytes: " + dumpHexString(keyBytes));
|
Log.d(TAG, "keyBytes: " + dumpHexString(keyBytes));
|
||||||
final ByteBuffer keyByteBuffer = ByteBuffer.wrap(keyBytes);
|
final ByteBuffer keyByteBuffer = ByteBuffer.wrap(keyBytes);
|
||||||
keyByteBuffer.order(ByteOrder.nativeOrder());
|
keyByteBuffer.order(ByteOrder.nativeOrder());
|
||||||
final Tether4Key tether4Key = Struct.parse(Tether4Key.class, keyByteBuffer);
|
final K k = Struct.parse(keyClass, keyByteBuffer);
|
||||||
Log.w(TAG, "tether4Key: " + tether4Key);
|
|
||||||
|
|
||||||
final byte[] valueBytes = Base64.decode(keyValueStrs[1], Base64.DEFAULT);
|
final byte[] valueBytes = Base64.decode(keyValueStrs[1], Base64.DEFAULT);
|
||||||
Log.d(TAG, "valueBytes: " + dumpHexString(valueBytes));
|
Log.d(TAG, "valueBytes: " + dumpHexString(valueBytes));
|
||||||
final ByteBuffer valueByteBuffer = ByteBuffer.wrap(valueBytes);
|
final ByteBuffer valueByteBuffer = ByteBuffer.wrap(valueBytes);
|
||||||
valueByteBuffer.order(ByteOrder.nativeOrder());
|
valueByteBuffer.order(ByteOrder.nativeOrder());
|
||||||
final Tether4Value tether4Value = Struct.parse(Tether4Value.class, valueByteBuffer);
|
final V v = Struct.parse(valueClass, valueByteBuffer);
|
||||||
Log.w(TAG, "tether4Value: " + tether4Value);
|
|
||||||
|
|
||||||
return new Pair<>(tether4Key, tether4Value);
|
return new Pair<>(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private HashMap<Tether4Key, Tether4Value> dumpIpv4UpstreamMap() throws Exception {
|
private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
|
||||||
final String rawMapStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE,
|
Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
|
||||||
DUMPSYS_TETHERING_RAWMAP_ARG);
|
throws Exception {
|
||||||
final HashMap<Tether4Key, Tether4Value> map = new HashMap<>();
|
final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
|
||||||
|
final String rawMapStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args);
|
||||||
|
final HashMap<K, V> map = new HashMap<>();
|
||||||
|
|
||||||
for (final String line : rawMapStr.split(LINE_DELIMITER)) {
|
for (final String line : rawMapStr.split(LINE_DELIMITER)) {
|
||||||
final Pair<Tether4Key, Tether4Value> rule = parseTether4KeyValue(line.trim());
|
final Pair<K, V> rule = parseMapKeyValue(keyClass, valueClass, line.trim());
|
||||||
map.put(rule.first, rule.second);
|
map.put(rule.first, rule.second);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private HashMap<Tether4Key, Tether4Value> pollIpv4UpstreamMapFromDump() throws Exception {
|
private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
|
||||||
|
Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
|
||||||
|
throws Exception {
|
||||||
for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
|
for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
|
||||||
final HashMap<Tether4Key, Tether4Value> map = dumpIpv4UpstreamMap();
|
final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
|
||||||
if (!map.isEmpty()) return map;
|
if (!map.isEmpty()) return map;
|
||||||
|
|
||||||
Thread.sleep(DUMP_POLLING_INTERVAL_MS);
|
Thread.sleep(DUMP_POLLING_INTERVAL_MS);
|
||||||
|
|||||||
Reference in New Issue
Block a user