Merge changes from topic "cookie_tag_map_raw_dump"

* changes:
  Add option to dump cookieTagMap in base64 format
  Use BpfDump#fromBase64EncodedString
This commit is contained in:
Motomu Utsumi 2022-08-24 03:16:53 +00:00 • committed by Gerrit Code Review
commit 9988869442
3 changed files with 85 additions and 33 deletions

View file

@ -30,7 +30,6 @@ import static android.system.OsConstants.IPPROTO_IP;
import static android.system.OsConstants.IPPROTO_IPV6;
import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.BpfDump.BASE64_DELIMITER;
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.ETHER_TYPE_IPV4;
@ -65,8 +64,6 @@ import android.os.HandlerThread;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.VintfRuntimeInfo;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
@ -76,6 +73,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
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.PacketBuilder;
import com.android.net.module.util.Struct;
@ -106,7 +104,6 @@ import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
@ -1273,32 +1270,6 @@ public class EthernetTetheringTest {
runUdp4Test(true /* verifyBpf */);
}
@Nullable
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);
String[] keyValueStrs = dumpStr.split(BASE64_DELIMITER);
if (keyValueStrs.length != 2 /* key + value */) {
fail("The length is " + keyValueStrs.length + " but expect 2. "
+ "Split string(s): " + TextUtils.join(",", keyValueStrs));
}
final byte[] keyBytes = Base64.decode(keyValueStrs[0], Base64.DEFAULT);
Log.d(TAG, "keyBytes: " + dumpHexString(keyBytes));
final ByteBuffer keyByteBuffer = ByteBuffer.wrap(keyBytes);
keyByteBuffer.order(ByteOrder.nativeOrder());
final K k = Struct.parse(keyClass, keyByteBuffer);
final byte[] valueBytes = Base64.decode(keyValueStrs[1], Base64.DEFAULT);
Log.d(TAG, "valueBytes: " + dumpHexString(valueBytes));
final ByteBuffer valueByteBuffer = ByteBuffer.wrap(valueBytes);
valueByteBuffer.order(ByteOrder.nativeOrder());
final V v = Struct.parse(valueClass, valueByteBuffer);
return new Pair<>(k, v);
}
@NonNull
private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
@ -1309,7 +1280,8 @@ public class EthernetTetheringTest {
final HashMap<K, V> map = new HashMap<>();
for (final String line : rawMapStr.split(LINE_DELIMITER)) {
final Pair<K, V> rule = parseMapKeyValue(keyClass, valueClass, line.trim());
final Pair<K, V> rule =
BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
map.put(rule.first, rule.second);
}
return map;

View file

@ -154,6 +154,7 @@ import com.android.internal.util.FileRotator;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.BestClock;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.BpfDump;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
@ -2532,6 +2533,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// usage: dumpsys netstats --full --uid --tag --poll --checkin
final boolean poll = argSet.contains("--poll") || argSet.contains("poll");
final boolean checkin = argSet.contains("--checkin");
final boolean bpfRawMap = argSet.contains("--bpfRawMap");
final boolean fullHistory = argSet.contains("--full") || argSet.contains("full");
final boolean includeUid = argSet.contains("--uid") || argSet.contains("detail");
final boolean includeTag = argSet.contains("--tag") || argSet.contains("detail");
@ -2573,6 +2575,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return;
}
if (bpfRawMap) {
dumpRawMapLocked(pw, args);
return;
}
pw.println("Directory:");
pw.increaseIndent();
pw.println(mStatsDir);
@ -2743,6 +2750,38 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
proto.flush();
}
private <K extends Struct, V extends Struct> void dumpRawMap(IBpfMap<K, V> map,
IndentingPrintWriter pw) throws ErrnoException {
if (map == null) {
pw.println("Map is null");
return;
}
if (map.isEmpty()) {
pw.println("No entries");
return;
}
// If there is a concurrent entry deletion, value could be null. http://b/220084230.
// Also, map.forEach could restart iteration from the beginning and dump could contain
// duplicated entries. User of this dump needs to take care of the duplicated entries.
map.forEach((k, v) -> {
if (v != null) {
pw.println(BpfDump.toBase64EncodedString(k, v));
}
});
}
@GuardedBy("mStatsLock")
private void dumpRawMapLocked(final IndentingPrintWriter pw, final String[] args) {
if (CollectionUtils.contains(args, "--cookieTagMap")) {
try {
dumpRawMap(mCookieTagMap, pw);
} catch (ErrnoException e) {
pw.println("Error dumping cookieTag map: " + e);
}
return;
}
}
private static void dumpInterfaces(ProtoOutputStream proto, long tag,
ArrayMap<String, NetworkIdentitySet> ifaces) {
for (int i = 0; i < ifaces.size(); i++) {

View file

@ -122,6 +122,7 @@ import android.provider.Settings;
import android.system.ErrnoException;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
@ -130,8 +131,10 @@ import androidx.test.filters.SmallTest;
import com.android.connectivity.resources.R;
import com.android.internal.util.FileRotator;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.net.module.util.BpfDump;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.U32;
import com.android.net.module.util.Struct.U8;
import com.android.net.module.util.bpf.CookieTagMapKey;
@ -168,6 +171,7 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -212,6 +216,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
private static final long WAIT_TIMEOUT = 2 * 1000; // 2 secs
private static final int INVALID_TYPE = -1;
private static final String DUMPSYS_BPF_RAW_MAP = "--bpfRawMap";
private static final String DUMPSYS_COOKIE_TAG_MAP = "--cookieTagMap";
private static final String LINE_DELIMITER = "\\n";
private long mElapsedRealtime;
private File mStatsDir;
@ -2333,12 +2342,27 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
dump.contains(message));
}
private String getDump() {
private String getDump(final String[] args) {
final StringWriter sw = new StringWriter();
mService.dump(new FileDescriptor(), new PrintWriter(sw), new String[]{});
mService.dump(new FileDescriptor(), new PrintWriter(sw), args);
return sw.toString();
}
private String getDump() {
return getDump(new String[]{});
}
private <K extends Struct, V extends Struct> Map<K, V> parseBpfRawMap(
Class<K> keyClass, Class<V> valueClass, String dumpStr) {
final HashMap<K, V> map = new HashMap<>();
for (final String line : dumpStr.split(LINE_DELIMITER)) {
final Pair<K, V> keyValue =
BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
map.put(keyValue.first, keyValue.second);
}
return map;
}
@Test
public void testDumpCookieTagMap() throws ErrnoException {
initBpfMapsWithTagData(UID_BLUE);
@ -2349,6 +2373,23 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
assertDumpContains(dump, "cookie=3002 tag=0x2 uid=1002");
}
@Test
public void testDumpCookieTagMapBpfRawMap() throws ErrnoException {
initBpfMapsWithTagData(UID_BLUE);
final String dump = getDump(new String[]{DUMPSYS_BPF_RAW_MAP, DUMPSYS_COOKIE_TAG_MAP});
Map<CookieTagMapKey, CookieTagMapValue> cookieTagMap = parseBpfRawMap(
CookieTagMapKey.class, CookieTagMapValue.class, dump);
final CookieTagMapValue val1 = cookieTagMap.get(new CookieTagMapKey(2002));
assertEquals(1, val1.tag);
assertEquals(1002, val1.uid);
final CookieTagMapValue val2 = cookieTagMap.get(new CookieTagMapKey(3002));
assertEquals(2, val2.tag);
assertEquals(1002, val2.uid);
}
@Test
public void testDumpUidCounterSetMap() throws ErrnoException {
initBpfMapsWithTagData(UID_BLUE);