Merge "Fix network usage stats on 464xlat tethered." into pi-dev

This commit is contained in:
Remi NGUYEN VAN
2018-04-02 23:09:50 +00:00
committed by Android (Google) Code Review
3 changed files with 92 additions and 51 deletions

View File

@@ -31,6 +31,7 @@ import java.io.CharArrayWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -97,6 +98,11 @@ public class NetworkStats implements Parcelable {
/** Denotes a request for stats at the interface and UID level. */ /** Denotes a request for stats at the interface and UID level. */
public static final int STATS_PER_UID = 1; public static final int STATS_PER_UID = 1;
private static final String CLATD_INTERFACE_PREFIX = "v4-";
// Delta between IPv4 header (20b) and IPv6 header (40b).
// Used for correct stats accounting on clatd interfaces.
private static final int IPV4V6_HEADER_DELTA = 20;
// TODO: move fields to "mVariable" notation // TODO: move fields to "mVariable" notation
/** /**
@@ -758,6 +764,75 @@ public class NetworkStats implements Parcelable {
return result; return result;
} }
/**
* Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
*
* <p>This mutates both base and stacked traffic stats, to account respectively for
* double-counted traffic and IPv4/IPv6 header size difference.
*
* <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
* packet on the stacked interface, and once as translated to an IPv6 packet on the
* base interface. For correct stats accounting on the base interface, every 464xlat
* packet needs to be subtracted from the root UID on the base interface both for tx
* and rx traffic (http://b/12249687, http:/b/33681750).
*
* <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
* {@code ConcurrentHashMap}
* @param baseTraffic Traffic on the base interfaces. Will be mutated.
* @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
*/
public static void apply464xlatAdjustments(NetworkStats baseTraffic,
NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
// Total 464xlat traffic to subtract from uid 0 on all base interfaces.
// stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
// For recycling
Entry entry = null;
Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
for (int i = 0; i < stackedTraffic.size; i++) {
entry = stackedTraffic.getValues(i, entry);
if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
continue;
}
final String baseIface = stackedIfaces.get(entry.iface);
if (baseIface == null) {
continue;
}
// Subtract any 464lat traffic seen for the root UID on the current base interface.
adjust.iface = baseIface;
adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
adjust.rxPackets = -entry.rxPackets;
adjust.txPackets = -entry.txPackets;
adjustments.combineValues(adjust);
// For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
// on the stacked interface with prefix "v4-" and drops the IPv6 header size after
// unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
// difference for all packets (http://b/12249687, http:/b/33681750).
entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
stackedTraffic.setValues(i, entry);
}
baseTraffic.combineAllValues(adjustments);
}
/**
* Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
*
* <p>This mutates the object this method is called on. Equivalent to calling
* {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as
* base and stacked traffic.
* @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
*/
public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
apply464xlatAdjustments(this, this, stackedIfaces);
}
/** /**
* Return total statistics grouped by {@link #iface}; doesn't mutate the * Return total statistics grouped by {@link #iface}; doesn't mutate the
* original structure. * original structure.

View File

@@ -56,11 +56,6 @@ public class NetworkStatsFactory {
private static final boolean USE_NATIVE_PARSING = true; private static final boolean USE_NATIVE_PARSING = true;
private static final boolean SANITY_CHECK_NATIVE = false; private static final boolean SANITY_CHECK_NATIVE = false;
private static final String CLATD_INTERFACE_PREFIX = "v4-";
// Delta between IPv4 header (20b) and IPv6 header (40b).
// Used for correct stats accounting on clatd interfaces.
private static final int IPV4V6_HEADER_DELTA = 20;
/** Path to {@code /proc/net/dev}. */ /** Path to {@code /proc/net/dev}. */
private final File mStatsIfaceDev; private final File mStatsIfaceDev;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */ /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
@@ -96,7 +91,7 @@ public class NetworkStatsFactory {
* {@link #noteStackedIface(String, String)}, but only interfaces noted before this method * {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
* is called are guaranteed to be included. * is called are guaranteed to be included.
*/ */
public static String[] augmentWithStackedInterfacesLocked(@Nullable String[] requiredIfaces) { public static String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
if (requiredIfaces == NetworkStats.INTERFACES_ALL) { if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
return null; return null;
} }
@@ -118,6 +113,15 @@ public class NetworkStatsFactory {
return relatedIfaces.toArray(outArray); return relatedIfaces.toArray(outArray);
} }
/**
* Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
* @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
*/
public static void apply464xlatAdjustments(NetworkStats baseTraffic,
NetworkStats stackedTraffic) {
NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces);
}
@VisibleForTesting @VisibleForTesting
public static void clearStackedIfaces() { public static void clearStackedIfaces() {
sStackedIfaces.clear(); sStackedIfaces.clear();
@@ -289,48 +293,10 @@ public class NetworkStatsFactory {
NetworkStats lastStats) throws IOException { NetworkStats lastStats) throws IOException {
final NetworkStats stats = final NetworkStats stats =
readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats); readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
// Total 464xlat traffic to subtract from uid 0 on all base interfaces.
// sStackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
final NetworkStats adjustments = new NetworkStats(0, sStackedIfaces.size());
NetworkStats.Entry entry = null; // For recycling // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
// TODO: remove this and only apply adjustments in NetworkStatsService.
// For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4 stats.apply464xlatAdjustments(sStackedIfaces);
// packet on the stacked interface, and once as translated to an IPv6 packet on the
// base interface. For correct stats accounting on the base interface, every 464xlat
// packet needs to be subtracted from the root UID on the base interface both for tx
// and rx traffic (http://b/12249687, http:/b/33681750).
for (int i = 0; i < stats.size(); i++) {
entry = stats.getValues(i, entry);
if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
continue;
}
final String baseIface = sStackedIfaces.get(entry.iface);
if (baseIface == null) {
continue;
}
NetworkStats.Entry adjust =
new NetworkStats.Entry(baseIface, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
// Subtract any 464lat traffic seen for the root UID on the current base interface.
adjust.rxBytes -= (entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
adjust.txBytes -= (entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
adjust.rxPackets -= entry.rxPackets;
adjust.txPackets -= entry.txPackets;
adjustments.combineValues(adjust);
// For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
// on the stacked interface with prefix "v4-" and drops the IPv6 header size after
// unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
// difference for all packets (http://b/12249687, http:/b/33681750).
entry.rxBytes = entry.rxPackets * IPV4V6_HEADER_DELTA;
entry.txBytes = entry.txPackets * IPV4V6_HEADER_DELTA;
entry.rxPackets = 0;
entry.txPackets = 0;
stats.combineValues(entry);
}
stats.combineAllValues(adjustments);
return stats; return stats;
} }

View File

@@ -148,7 +148,6 @@ import java.time.ZoneOffset;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Collect and persist detailed network statistics, and provide this data to * Collect and persist detailed network statistics, and provide this data to
@@ -769,7 +768,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public NetworkStats getDetailedUidStats(String[] requiredIfaces) { public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
try { try {
final String[] ifacesToQuery = final String[] ifacesToQuery =
NetworkStatsFactory.augmentWithStackedInterfacesLocked(requiredIfaces); NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
return getNetworkStatsUidDetail(ifacesToQuery); return getNetworkStatsUidDetail(ifacesToQuery);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.wtf(TAG, "Error compiling UID stats", e); Log.wtf(TAG, "Error compiling UID stats", e);
@@ -1527,12 +1526,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private NetworkStats getNetworkStatsUidDetail(String[] ifaces) private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
throws RemoteException { throws RemoteException {
// TODO: remove 464xlat adjustments from NetworkStatsFactory and apply all at once here.
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL, final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
ifaces); ifaces);
// fold tethering stats and operations into uid snapshot // fold tethering stats and operations into uid snapshot
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID); final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL); tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
uidSnapshot.combineAllValues(tetherSnapshot); uidSnapshot.combineAllValues(tetherSnapshot);
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService( final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
@@ -1542,13 +1543,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID); final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
if (vtStats != null) { if (vtStats != null) {
vtStats.filter(UID_ALL, ifaces, TAG_ALL); vtStats.filter(UID_ALL, ifaces, TAG_ALL);
NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats);
uidSnapshot.combineAllValues(vtStats); uidSnapshot.combineAllValues(vtStats);
} }
uidSnapshot.combineAllValues(mUidOperations); uidSnapshot.combineAllValues(mUidOperations);
// TODO: apply tethering & VC 464xlat adjustments here
return uidSnapshot; return uidSnapshot;
} }